<?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:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-3016850660716023308</atom:id><lastBuildDate>Thu, 12 Nov 2009 06:25:47 +0000</lastBuildDate><title>Jess Chadwick: Coder</title><description>Am I an expert?  Nah... but I play one on the Internet!</description><link>http://jesschadwick.blogspot.com/</link><managingEditor>jesschadwick@gmail.com (Jess Chadwick)</managingEditor><generator>Blogger</generator><openSearch:totalResults>59</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><creativeCommons:license>http://creativecommons.org/licenses/by/2.0/</creativeCommons:license><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/JessChadwick" type="application/rss+xml" /><feedburner:emailServiceId>JessChadwick</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-3067269708603762456</guid><pubDate>Tue, 10 Jun 2008 04:12:00 +0000</pubDate><atom:updated>2008-06-10T00:19:51.300-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><title>ASP.NET MVC: Using Custom View Engines and Filters for AJAX</title><description>&lt;p&gt;Way back in the time of ASP.NET MVC Preview 2 (last month), I was preparing for a presentation and decided that it would be nice to show just how simple it is to make your own View Engine.&amp;#160; Having found &lt;a href="http://www.fragmentedcode.com/2008/03/27/jsonviewengine-for-aspnet-mvc-framework/"&gt;David Higgins' JsonViewEngine&lt;/a&gt; a few days earlier, I decided to expand a little upon it and add the option to emit either XML or JSON using the same engine.&amp;#160; After a little while, I had something looking like this that supported both:&lt;/p&gt; &lt;fieldset class="code"&gt;&lt;legend&gt;SerializableViewEngine&lt;/legend&gt;    &lt;p&gt;public class SerializableViewEngine : IViewEngine      &lt;br /&gt;{       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public void RenderView(ViewContext viewContext)       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; string method = viewContext.RouteData.Values[&amp;quot;responseType&amp;quot;] as string; &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; string serializedData = string.Empty;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; switch (method.ToLowerInvariant())       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; case &amp;quot;json&amp;quot;:       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; serializedData = new JavaScriptSerializer().Serialize(viewContext.ViewData);       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; break;       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; case &amp;quot;xml&amp;quot;:       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; serializedData = SerializeToXml(viewContext.ViewData);       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; break;       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; viewContext.HttpContext.Response.Clear();      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; viewContext.HttpContext.Response.Write(serializedData);       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; } &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; protected static string SerializeToXml(object o)      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; StringBuilder sb = new StringBuilder();       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; using (TextWriter writer = new StringWriter(sb))       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; new XmlSerializer(o.GetType()).Serialize(writer, o);       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return sb.ToString();       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }       &lt;br /&gt;} &lt;/p&gt; &lt;/fieldset&gt;   &lt;p&gt;As far as building a custom ViewEngine goes, I figure this is a great example - other than the fact that I extracted my &amp;quot;SerializeToXml&amp;quot; logic out, this is a one-method override!&amp;#160; At first glance this (and David's) implementation may seem a little &amp;quot;underpowered&amp;quot; because of how few lines it took, but if you disassemble the System.Web.Mvc assembly or look at the MVCContrib code, you'll see that &lt;strong&gt;even real-world ViewEngine implementations can be this simple!&lt;/strong&gt;&amp;#160; I guess that's one of the nice side-effects of separation of concerns - when big things are broken up into a bunch of pieces, those pieces are usually pretty small!&lt;/p&gt;  &lt;p&gt;As for the details of this particular example, you'll notice that it checks the RouteData dictionary (populated from the routing tables you specified elsewhere) to see if it contains &amp;quot;responseType&amp;quot; (e.g. if one of your routes was &amp;quot;{responseType}/{controller}/{action}&amp;quot; and the url that was called looked something like &amp;quot;/json/products/categories&amp;quot;) and try to honor it by serializing the ViewData that was passed from the controller into the requested result.&amp;#160; Once it has serialized the data, it just writes it directly to the Response and you're done!&amp;#160; You could then use this View Engine to render out all the async calls all day and night, just by setting the ViewEngine property on the controller instance like so:&lt;/p&gt; &lt;fieldset&gt;&lt;legend&gt;Setting the ViewEngine&lt;/legend&gt;    &lt;p class="code"&gt;public void AsyncCategories()      &lt;br /&gt;{       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; var categories = repository.GetCategories();       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; this.ViewEngine = new SerializableViewEngine();       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; return RenderView(&amp;quot;Category&amp;quot;, categories);       &lt;br /&gt;} &lt;/p&gt; &lt;/fieldset&gt;   &lt;p&gt;Simple as that, right?&amp;#160; Well, sure... but what happens when you end up making duplicate Actions for your &amp;quot;normal&amp;quot; browser and AJAX requests that have all the same logic with the exception of that &lt;em&gt;one&lt;/em&gt; line?&amp;#160; The line that sets the ViewEngine is hard-coded in there, so you &lt;em&gt;have&lt;/em&gt; to have separate Actions, right?&amp;#160; &lt;/p&gt; &lt;strong&gt;Enter the ActionFilter!&lt;/strong&gt;   &lt;p&gt;Wrong!&amp;#160; After thinking about it for a minute I realized that this is the &lt;em&gt;perfect&lt;/em&gt; place to use an ActionFilter (which was a good thing, because I just so happened to need a code example for that, too)!&amp;#160; And so, I got to coding and came up with this guy:&lt;/p&gt; &lt;fieldset&gt;&lt;legend&gt;SerializableViewDataAttribute&lt;/legend&gt;    &lt;p&gt;public class SerializableViewDataAttribute : ActionFilterAttribute      &lt;br /&gt;{       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; public override void OnActionExecuting(FilterExecutingContext filterContext)       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var controller = ((Controller)filterContext.Controller); &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if (filterContext.RouteData.Values.ContainsKey(&amp;quot;responseType&amp;quot;))      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; controller.ViewEngine = new SerializableViewEngine(); &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; base.OnActionExecuting(filterContext);      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }       &lt;br /&gt;} &lt;/p&gt; &lt;/fieldset&gt;   &lt;p&gt;As you can &lt;a href="http://quickstarts.asp.net/3-5-extensions/mvc/ActionFiltering.aspx"&gt;read about here&lt;/a&gt; (if you haven't already), ActionFilters are the perfect way to &amp;quot;inject&amp;quot; behaviors into your controllers.&amp;#160; I like to compare them to the ol' Web Forms ASP.NET Page Lifecycle events such as PreInit, Init, PreRender, Render, etc. that let you plug in to the various stages of processing a request and allow you to manipulate it accordingly.&amp;#160; For example, by overriding the &lt;em&gt;OnActionExecuting&lt;/em&gt; method on the ActionFilterAttribute as shown above, you can plug into the request after it's reached the controller, but before any action has actually been taken on it.&amp;#160; I believe I could have also used &lt;em&gt;OnActionExecuted&lt;/em&gt; to accomplish the same thing since I'm only setting the ViewEngine, but that just didn't &amp;quot;feel&amp;quot; right.&lt;/p&gt;  &lt;p&gt;Using this new custom ActionFilter, I could apply it at the Action level - or the Controller level if I wanted to apply it to &lt;em&gt;all&lt;/em&gt; of the controller's actions - and get rid of those silly duplicate Actions.&amp;#160; Now, instead of having the &lt;em&gt;AsyncCategories&lt;/em&gt; Action shown above (which is assumedly just a duplicate of some other Action, perhaps called &lt;em&gt;Categories&lt;/em&gt;) that explicitly overrides the ViewEngine with every request, I can now drop the line that sets the View Engine and let my new ActionFilter take care of setting it for me when it is appropriate!&amp;#160; In other words, the code above now turns into the code below, which handles both &amp;quot;regular&amp;quot; and AJAX calls with the &lt;em&gt;same code&lt;/em&gt;!&lt;/p&gt; &lt;fieldset&gt;&lt;legend&gt;Removing the ViewEngine code from the AsyncCategories Action&lt;/legend&gt;    &lt;p class="code"&gt;[SerializableViewData]      &lt;br /&gt;public void Categories()       &lt;br /&gt;{       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; var categories = repository.GetCategories();       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; return RenderView(&amp;quot;Category&amp;quot;, categories);       &lt;br /&gt;} &lt;/p&gt; &lt;/fieldset&gt;   &lt;p&gt;The first time I successfully saw the result of running this demo was the first time the power of ASP.NET MVC really hit me.&amp;#160; This whole example was just a bunch of really small and straight-forward (dare I say 'simple'?) pieces that, when put together, create some really amazing and powerful results.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Grab the Code and See For Yourself!&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;You can see all of this stuff in action by downloading the code from my website:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://jchad.mediafreak.org/demos/aspnet/mvc/ASPNETMVC_Preview2_ViewEngineDemo.zip"&gt;ASP.NET MVC Custom ViewEngine and ActionFilter Demo&lt;/a&gt; (118 Kb) &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;I welcome any and all feedback y'all can throw my way.&amp;#160; Enjoy!&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;UPDATE:&lt;/em&gt;&amp;#160; As the beginning of the post says, these are what you can do with the Preview 2 bits.&amp;#160; With the addition of the ActionResult in Preview 3 and the related methods on ActionFilter, you can actually ditch the custom ViewEngine altogether and just use the ActionFilter!&amp;#160; Not the ActionFilter I show here, mind you, but an updated version I will show in an up-coming blog.&amp;#160; :)&lt;/p&gt;&lt;/blockquote&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-3067269708603762456?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=TzyQIqFKWdQ:9cfcIAcx3nI:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=TzyQIqFKWdQ:9cfcIAcx3nI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=TzyQIqFKWdQ:9cfcIAcx3nI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=TzyQIqFKWdQ:9cfcIAcx3nI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/TzyQIqFKWdQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/TzyQIqFKWdQ/aspnet-mvc-using-custom-view-engines.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/06/aspnet-mvc-using-custom-view-engines.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-8392267942585035239</guid><pubDate>Thu, 19 Jun 2008 06:47:00 +0000</pubDate><atom:updated>2008-06-19T11:33:54.579-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Community</category><title>Inaugural NJDOTNET Agile Meeting **Tonight**</title><description>&lt;p&gt;As I announced at the last few weeks' NJDOTNET Study &amp;amp; User Group meetings, we'll be having our very first &lt;em&gt;NJDOTNET Agile&lt;/em&gt; meeting tonight, &lt;strong&gt;Thursday, June 16th starting at 6:30 PM&lt;/strong&gt; in the Infragistics auditorium in East Windsor (the same location as the regular NJDOTNET user group meetings). Here's a little bit about the group:&lt;/p&gt;  &lt;p&gt;Taking a cue from both the growing ALT.NET movement and quite a few members of the main NJDOTNET user group, I've decided to augment the monthly meetings of our established user group to create a more interactive, discussion-based meeting. These &lt;a href="http://en.wikipedia.org/wiki/Open_Space_Technology"&gt;Open Spaces&lt;/a&gt; meetings will occur once a month, on the weeks following the regular User Group (the third Thursday of the month). Hopefully the addition of these meetings will allow everyone to get the benefit of a different style of interaction and learning that the &amp;#8220;Lecture-Learn&amp;#8221; style of the main user group cannot provide.&lt;/p&gt;  &lt;p&gt;I'll see everyone there!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-8392267942585035239?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=eXnqmskx8lk:sJVX8X9d73k:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=eXnqmskx8lk:sJVX8X9d73k:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=eXnqmskx8lk:sJVX8X9d73k:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=eXnqmskx8lk:sJVX8X9d73k:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/eXnqmskx8lk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/eXnqmskx8lk/inaugural-njdotnet-agile-meeting.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/06/inaugural-njdotnet-agile-meeting.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-448087549033495491</guid><pubDate>Wed, 25 Jun 2008 18:52:00 +0000</pubDate><atom:updated>2008-06-25T20:27:05.015-04:00</atom:updated><title>Where the heck can I download Firefox 2.0!?</title><description>&lt;p&gt;Now that Firefox 3.0 is finally released, Mozilla has done a real good job of clearing out any mention of it from their website.&amp;#160; Thing is, we web developers still need to test on it!&lt;/p&gt;  &lt;p&gt;Well, after a bit of querystring hacking (simply replacing &amp;quot;product=firefox-3.0&amp;quot; with &amp;quot;product=firefox-2.0.0.14&amp;quot;), I came up with this link, which should get you what you need:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://download.mozilla.org/?product=firefox-2.0.0.14&amp;os=win&amp;lang=en-US" target="_blank"&gt;Download Firefox 2.0.0.14&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-448087549033495491?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=ZwiLEcZSK40:bLXnlQ8LceU:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=ZwiLEcZSK40:bLXnlQ8LceU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=ZwiLEcZSK40:bLXnlQ8LceU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=ZwiLEcZSK40:bLXnlQ8LceU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/ZwiLEcZSK40" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/ZwiLEcZSK40/where-heck-can-i-download-firefox-20.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/06/where-heck-can-i-download-firefox-20.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-5030057983018103017</guid><pubDate>Wed, 02 Jul 2008 03:59:00 +0000</pubDate><atom:updated>2008-11-16T23:32:25.488-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><title>Use ASP.NET MVC ActionFilters to Render AJAX Responses!</title><description>&lt;p&gt;In my &lt;a href="http://jesschadwick.blogspot.com/2008/06/aspnet-mvc-using-custom-view-engines.html"&gt;previous blog post on ASP.NET MVC&lt;/a&gt;, I walked through an example based on the MVC Preview 2 bits which showed how to make your own custom view engines and ActionFilters.&amp;#160; I also proved (or at least hope I proved) just how powerful these parts of the framework can be, especially when used in conjunction with each other.&amp;#160; The truth is that - while I thought they made great examples on how to extend the base parts of the framework - the whole thing seemed kind of kludgie to me.&amp;#160; What I mean is that it felt kind of awkward having to use the &lt;strong&gt;ActionFilter&lt;/strong&gt; to change which &lt;strong&gt;ViewEngine&lt;/strong&gt; is being used on the fly.&amp;#160; I don't know, maybe I just feel like view engines should be more closely tied to the controller and shouldn't just be chosen willy-nilly like that!&lt;/p&gt; &lt;p&gt;This was all before the introduction of the &lt;strong&gt;ActionResult&lt;/strong&gt; in MVC Preview 3.&amp;#160; Oh man, was this a great addition!&amp;#160; Expanding the notion of being &amp;quot;loosely coupled&amp;quot;, the controller action's job is no longer to execute logic and chose which view to display-- ok, well, it is...&amp;#160; only now its main responsibility is returning an &lt;strong&gt;ActionResult&lt;/strong&gt;, which is basically a command object that &lt;em&gt;tells the framework what to do&lt;/em&gt; instead the controller taking that action itself.&amp;#160; This not only helps us to decouple the controller (allowing for easier testing, etc.), it also lets us know what the controller wants the framework to do &lt;em&gt;before it happens&lt;/em&gt;.&amp;#160; When you think of it, this is pretty powerful stuff, and gives us access to a whole new part of the process in which can inject additional logic.&amp;#160; And inject it we shall...&lt;/p&gt;  &lt;h3&gt;The SerializableViewDataAttribute Revisited&lt;/h3&gt; &lt;p&gt;In my previous post, the attribute that I adorned my &lt;strong&gt;Action&lt;/strong&gt;s with was pretty stupid.&amp;#160; It just said, &amp;quot;Is this an AJAX request? If so, set our custom view engine.&amp;quot;&amp;#160; Pretty much all of the meaningful (and interesting) logic happened in our custom view engine.&amp;#160; Now, with the introduction of the &lt;strong&gt;ActionResult&lt;/strong&gt; we no longer have to wait until the view engine phase to get access to (and override) the way our data is returned to the client - we can hijack it in the attribute, and manipulate the Action's result!&amp;#160; Before I confuse you any more, here's the code:&lt;/p&gt; &lt;fieldset&gt;&lt;legend&gt;SerializableViewDataAttribute: Preview 3 style&lt;/legend&gt; &lt;div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:b9b766a0-8fbe-4386-9467-4dd08d5b8c75" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#"&gt;public class SerializableViewDataAttribute : ActionFilterAttribute&lt;br /&gt;{&lt;br /&gt;    public override void OnActionExecuted(ActionExecutedContext filterContext)&lt;br /&gt;    {&lt;br /&gt;        // Get the (lowercase) render mode from the querystring&lt;br /&gt;        var method = filterContext.HttpContext.Request.QueryString["renderMode"] ?? string.Empty;&lt;br /&gt;        method = method.ToLowerInvariant();&lt;br /&gt;&lt;br /&gt;        // Get the view data from the context (using a custom extension method defined elsewhere)&lt;br /&gt;        var viewData = filterContext.GetViewData();&lt;br /&gt;&lt;br /&gt;        // If this isn't a serializable request or it has no data, just return&lt;br /&gt;        if (string.IsNullOrEmpty(method) || viewData == null) return;&lt;br /&gt;&lt;br /&gt;        // Decide which method to use&lt;br /&gt;        switch (method)&lt;br /&gt;        {&lt;br /&gt;            case ("xml"):&lt;br /&gt;                filterContext.Result = getXmlResult(viewData);&lt;br /&gt;                break;&lt;br /&gt;            case ("json"):&lt;br /&gt;                filterContext.Result = getJsonResult(viewData);&lt;br /&gt;                break;&lt;br /&gt;            case ("partial"):&lt;br /&gt;                // If this is supposed to be a partial request, just change the view name.&lt;br /&gt;                // Obviously, this implies that this view exists (in addition to your&lt;br /&gt;                // original view that the controller was pointing to to begin with.&lt;br /&gt;                var viewResult = ((ViewResult)filterContext.Result);&lt;br /&gt;                viewResult.ViewName = "Partial" + viewResult.ViewName;&lt;br /&gt;                break;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private static ActionResult getJsonResult(object viewData)&lt;br /&gt;    {&lt;br /&gt;        // Since there's a JsonResult Action, &lt;br /&gt;        // all we have to do is pass in the data&lt;br /&gt;        return new JsonResult { Data = viewData };&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private static ActionResult getXmlResult(object viewData)&lt;br /&gt;    {&lt;br /&gt;        // There's no "XmlResult" so we have to serialize it ourselves&lt;br /&gt;        var sb = new StringBuilder();&lt;br /&gt;        using (var writer = new StringWriter(sb))&lt;br /&gt;        {&lt;br /&gt;            new XmlSerializer(viewData.GetType()).Serialize(writer, viewData);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // ...then pass it into a ContentResult&lt;br /&gt;        return new ContentResult&lt;br /&gt;        {&lt;br /&gt;            ContentType = "text/xml",  // Be sure to set the ContentType!&lt;br /&gt;            Content = sb.ToString()&lt;br /&gt;        };&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/fieldset&gt; &lt;br /&gt;&lt;p&gt;As you can see, it is a &lt;em&gt;lot&lt;/em&gt; like the logic that was used to create my custom view engine in the last post.&amp;#160; In fact, I essentially copied and pasted it (and added support for partial rendering)!&amp;#160; Also, now that all of this logic lives in the &lt;strong&gt;ActionFilter&lt;/strong&gt; we have no more need for that pesky custom view engine.&amp;#160; So long, cruft!&lt;/p&gt;&lt;br /&gt;&lt;p&gt;For the benefit of those who may not have read the explanation of this logic in my previous blog post, I'll go ahead and repeat (as well as expand on) it here.&amp;#160; This version should actually be a lot easier to understand since the logic is now contained in one place instead of spread out between the &lt;strong&gt;ActionFilter&lt;/strong&gt; and the &lt;strong&gt;ViewEngine&lt;/strong&gt;.&amp;#160; Here's a basic run-down of what's going on:&lt;/p&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The first thing to note is that we're overriding the &lt;em&gt;OnActionExecuted&lt;/em&gt; method of the ActionFilter.&amp;#160; What this implies is that the controller action to which this attribute is applied has already completed, so we now have access to everything it's done: ViewData, TempData, ViewName, etc.&amp;#160; We could also have overridden the &lt;em&gt;OnResultExecuting&lt;/em&gt; method as well; this would be somewhat similar to the difference between the &lt;em&gt;PreRender&lt;/em&gt; and &lt;em&gt;Render&lt;/em&gt; events when using Web Forms. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Once the &amp;quot;event&amp;quot; has been triggered, the first thing we want to do is figure out what, exactly, is expected of us - AKA, which serialization mode (if any) we should be using.&amp;#160; In this implementation, I have decided to glean this information from the querystring, but you can just as easily use any other environment variable you'd like (such as the Route Data, cookies... whatever!).&lt;/li&gt;&lt;li&gt;Next, I get the ViewData from the context, if any, using the &lt;em&gt;filterContext.GetViewData()&lt;/em&gt; extension method.&amp;#160; Though this method is defined elsewhere and you can't see it in this snippet, it's not really all that interesting - it's just a convenience method that drills down into filterContext.Controller.ViewData (or ViewData.Model, if available) and returns it.&lt;/li&gt;&lt;li&gt;Check the render method that we got in Step #2 and if that or the ViewData is invalid, there's nothing for us to do, so we're done and we just return.&lt;/li&gt;  &lt;li&gt;Assuming we didn't bail out in Step #4, we then go on to overwrite the existing ActionResult (or its properties), depending on what the user has asked for.&amp;#160; This particular version supports:&lt;ol&gt;&lt;li&gt;&lt;strong&gt;JSON&lt;/strong&gt;:&amp;#160; Along with the introduction of the &lt;strong&gt;ActionResult&lt;/strong&gt; came the &lt;strong&gt;JsonResult&lt;/strong&gt;, which is a super-cool implementation of ActionResult that serializes whatever data you give to it into JSON to be rendered down to the browser.&amp;#160; As you can see in this example, this makes handling requests for JSON data &lt;em&gt;really&lt;/em&gt; simple and easy.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;XML:&lt;/strong&gt;&amp;#160; Though there is no &amp;quot;XmlResult&amp;quot; as there is for JSON, the MVC framework still provides a &lt;strong&gt;ContentResult&lt;/strong&gt; which is a lot like the &lt;strong&gt;JsonResult&lt;/strong&gt; in spirit.&amp;#160; Both of them allow us to send data in some form back to the browser without requiring a View to render it with.&amp;#160; Going back to our example - you can see that it takes a bit of work, but the end result is a raw batch of &lt;em&gt;just&lt;/em&gt; XML sent back to the client for processing.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Partial Rendering&lt;/strong&gt;:&amp;#160; I included this render mode as a bit of a teaser - I should have a follow-up post going in-depth about using partial rendering in ASP.NET MVC.&amp;#160; For now, I'll just say that you can think of it a lot like the ASP.NET AJAX &lt;strong&gt;UpdatePanel&lt;/strong&gt; server control...&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;That's it!&amp;#160; If we've gotten to this step and not hit any of the three types above, this particular filter will just let the request continue on and have no effects what-so-ever.&amp;#160; That probably means it'll just end up rendering out a fully-rendered page like any other normal (non-AJAX) request.&amp;#160; The important part to note here is that &lt;em&gt;this filter only injects behavior when it is appropriate&lt;/em&gt;.&amp;#160; Otherwise, things proceed as normal and noone even needs to know that these actions are AJAX-ified unless you want them to.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;I don't know about you, but the power that this kind of stuff gives us just makes my head spin!&amp;#160; Hopefully you find the above useful; if you do, please feel free to contact me and let me know, especially if you end up using it in one of your apps!&lt;/p&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;  &lt;p&gt;As always, you can check out all of the source code in my repository: &lt;a title="http://code.jesschadwick.com/" href="http://code.jesschadwick.com/"&gt;http://code.jesschadwick.com/&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;  &lt;p&gt;This particular piece of code can most easily be found at &lt;a href="http://code.jesschadwick.com/presentations/ASP.NET%20MVC/AJAX%20Demo/Ajax%20Demo/Filters/SerializableViewDataAttribute.cs"&gt;this direct link&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-5030057983018103017?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=p0jEmcSnwGo:hf-D6ep9eNU:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=p0jEmcSnwGo:hf-D6ep9eNU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=p0jEmcSnwGo:hf-D6ep9eNU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=p0jEmcSnwGo:hf-D6ep9eNU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/p0jEmcSnwGo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/p0jEmcSnwGo/use-aspnet-mvc-actionfilters-to-render_02.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/07/use-aspnet-mvc-actionfilters-to-render_02.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-7367746817463833403</guid><pubDate>Sat, 02 Aug 2008 06:20:00 +0000</pubDate><atom:updated>2008-08-02T02:20:56.773-04:00</atom:updated><title>The NJDOTNET Dojo is Born!</title><description>&lt;p&gt;If you're a developer in the Princeton, NJ area, you'll be pleased to know that the NJDOTNET organizers have decided to create yet &lt;i&gt;another&lt;/i&gt; group for your .NET learning pleasure!&amp;#160; Just this past Thursday, we had our first &lt;em&gt;NJDOTNET Dojo&lt;/em&gt; meeting, at which I gave a presentation on the Web Development Productivity Tools I use to get things done.&amp;#160; &lt;/p&gt;  &lt;p&gt;This new group will continue to meet on an on-going basis every Thursday night that there isn't another meeting (the main User Group or Agile discussion) going on.&amp;#160; Basically, if you're looking to learn about .NET, come on down to the Infragistics HQ on any given Thursday and you're bound to be satisfied!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.njdotnet.net/forums/t/87.aspx"&gt;See this forum thread for more details. &lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-7367746817463833403?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=BXCsHZ3ckwA:K-FHSzkBtAE:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=BXCsHZ3ckwA:K-FHSzkBtAE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=BXCsHZ3ckwA:K-FHSzkBtAE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=BXCsHZ3ckwA:K-FHSzkBtAE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/BXCsHZ3ckwA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/BXCsHZ3ckwA/njdotnet-dojo-is-born.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/08/njdotnet-dojo-is-born.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-415131785843926690</guid><pubDate>Wed, 10 Sep 2008 07:49:00 +0000</pubDate><atom:updated>2009-10-16T02:46:28.273-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Tips and Tricks</category><title>S-E-Ooooh, That's How You Do It!</title><description>&lt;p&gt;My boss, Kevin, and I headed out to Seattle last month (coincidentally during the time that some of our Infragistics buddies were heading up the speaker list at &lt;a href="http://www.devscovery.com/" target="_blank"&gt;DevScovery&lt;/a&gt;) to attend an &lt;a href="http://www.seomoz.org/expert" target="_blank"&gt;expert-level SEO seminar&lt;/a&gt; given by the bright folks over at &lt;a href="http://www.seomoz.org/" target="_blank"&gt;SEOmoz&lt;/a&gt;.&amp;#160; And, boy was it enlightening!&lt;/p&gt;  &lt;p&gt;Now, before I go any further, I'm going to have to state right up front that (until late last month) I have been wandering through my professional web career knowing only a bare minimum about SEO.&amp;#160; I &lt;em&gt;thought&lt;/em&gt; I understood basic concepts like linking and keyword placement - the more times you link to a page and repeat a word, the higher your search rank, right?&amp;#160; Unsurprisingly, it turns out to be a &lt;em&gt;bit&lt;/em&gt; more involved that that. :)&lt;/p&gt;  &lt;p&gt;There's no way I'm going to duplicate in a blog post or even a series of blog posts what I learned at that awesome seminar - if you're truly that interested, I would suggest signing up for the training or at the very least checking out the articles on SEOmoz.&amp;#160; However, I wanted to 'jot down' a few of my largest takeaways, because they really made me reevaluate my view on the way SEO works.&amp;#160; Since I have a knack for oversimplifying things (verging on naivety), I figured a bulleted list would do just the trick.&amp;#160; So, below you'll find my ridiculously simplified...&lt;/p&gt;  &lt;h3&gt;List of Things You Probably Already Knew About SEO But Didn't Realize How Important They Were (or at least I didn't...):&lt;/h3&gt;  &lt;ol&gt;   &lt;li&gt;     &lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Backlink" target="_blank"&gt;&lt;strong&gt;Backlinks&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&amp;#160; I'm sure it comes as no surprise that having other sites (especially the big/important ones) link to yours is beneficial, but I'd have to say the most unexpected thing I took away from this seminar was just how important they are!&amp;#160; From what I understand, these account for the vast majority of your &lt;a href="http://en.wikipedia.org/wiki/Pagerank" target="_blank"&gt;PageRank&lt;/a&gt;-ings - I'm talking &lt;em&gt;well&lt;/em&gt; over half.&amp;#160; That means you need to beg, borrow, cheat, and steal (wait, no... not those last two) to get everyone and their dog to link back to you (assuming their dog's site has a lot of traffic, of course!).&amp;#160; As always, the better their &lt;a href="http://en.wikipedia.org/wiki/Pagerank" target="_blank"&gt;PageRank&lt;/a&gt;, the better your Link Juice.&lt;/p&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;p&gt;Oh, yeah - &lt;a href="http://www.seomoz.org/blog/pagerank-link-patterns-the-new-flow-of-link-juice" target="_blank"&gt;&lt;strong&gt;Link Juice&lt;/strong&gt;&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Nofollow" target="_blank"&gt;&lt;strong&gt;nofollow&lt;/strong&gt;&lt;/a&gt;!&amp;#160;&amp;#160; To be honest, I'd never heard the term 'link juice' before.&amp;#160; But after hearing it explained, it was just so incredibly appropriate I really became endeared with it.&amp;#160; Put simply, think of the traffic to any one of your pages as water flowing into a pipe.&amp;#160; You start out with 10 gallons of water coming into one pipe (this landing page) from which it is split up and diverted to various other pipes (pages you're linking to from the landing page).&amp;#160; Each of these splits divides and cuts down the volume of the initial 10 gal., such that if you have, say, 10 links on your page each of them gets 1 gal. of that initial 10.&amp;#160; Now, if you play that concept all the way through each page, constantly splitting up the water on every page, you can imagine how quickly that 10 gallon deluge can become just a trickle...&amp;#160; Obviously, we'd like to see some pages on our site get more traffic than others, so how do we&amp;#160; keep our existing links so people can still navigate our site, yet &amp;quot;divert the water&amp;quot; to the pages that we think are the most important?&amp;#160; Fortunately, the answer is pretty simple - &lt;a href="http://en.wikipedia.org/wiki/Nofollow" target="_blank"&gt;the &amp;quot;rel='nofollow'&amp;quot; tag&lt;/a&gt;!&amp;#160; Placing this tag on any of your links basically tells search engines &amp;quot;this page really isn't important to me and I really don't want to waste my influence on it.&amp;quot;&amp;#160; It's kind of like &lt;a href="http://en.wikipedia.org/wiki/Mission:_Impossible" target="_blank"&gt;disavowing all knowledge&lt;/a&gt; of your pages... even if it quite obvious that they're worth something to you (since they still exist, after all).&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;       &lt;p&gt;I didn't come up with this concept, so my explanation of it probably sucks.&amp;#160; To really learn about it, I recommend visiting &lt;a href="http://www.seomoz.org/blog/pagerank-link-patterns-the-new-flow-of-link-juice" target="_blank"&gt;Rand Fishkin's great blog post on it&lt;/a&gt;.&lt;/p&gt;     &lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;     &lt;p&gt;Then there's plain ol' &lt;strong&gt;good, properly-formatted, and relevant content&lt;/strong&gt;...&amp;#160; Really, from what I could tell, those first two bullets are the major players in terms of tips &amp;amp; tricks.&amp;#160; The search engines are getting better at filtering out spammers every day through various techniques, so your ultimate defense is to create interesting content, and make use of standard best practices like putting your important keywords in your &amp;lt;title&amp;gt; and &amp;lt;h&lt;em&gt;x&lt;/em&gt;&amp;gt; tags, concentrating on creating relevant content, and trying to keep your markup light and semantically correct.&amp;#160; Simple... right?&amp;#160; &amp;quot;Easier said than done&amp;quot; is more like it!&lt;/p&gt;   &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;I had intended from the start for this to be a quick overview of what I took away from my recent training and nowhere near a &amp;quot;deep dive&amp;quot; into SEO.&amp;#160; But, to help you on your way, I've collected a few resources that might help you learn some more if I've happened to pique your interest... which I certainly hope I have!&lt;/p&gt;  &lt;h4&gt;Additional Resources:&lt;/h4&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Search_engine_optimization" target="_blank"&gt;Wikipedia:&amp;#160; Search Engine Optimization&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Pagerank" target="_blank"&gt;Wikipedia:&amp;#160; Google PageRank&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.seomoz.org/" target="_blank"&gt;SEOmoz&lt;/a&gt;:&amp;#160; &lt;a href="http://www.seomoz.org/blog" target="_blank"&gt;Blogs&lt;/a&gt;, &lt;a href="http://www.seomoz.org/articles" target="_blank"&gt;articles&lt;/a&gt;, &lt;a href="http://www.seomoz.org/tools" target="_blank"&gt;tools&lt;/a&gt; (albeit not free), &lt;a href="http://www.seomoz.org/expert" target="_blank"&gt;training&lt;/a&gt;... awesomeness.&amp;#160; I really think these guys are leading the industry. &lt;/li&gt;    &lt;li&gt;&lt;a href="http://tools.seobook.com/" target="_blank"&gt;SEOTools&lt;/a&gt;/&lt;a href="http://www.seobook.com/" target="_blank"&gt;SEOBook.com&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.seochat.com/" target="_blank"&gt;SEOChat.com&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.google.com/webmasters/" target="_blank"&gt;Google Webmaster portal&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;And so many more... &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Hope this all helps!&amp;#160; Oh, and please leave me a comment and let me know if it does - I'd love to hear about it!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-415131785843926690?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=KGjCxGS1MT0:jWFZUzgW6WY:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=KGjCxGS1MT0:jWFZUzgW6WY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=KGjCxGS1MT0:jWFZUzgW6WY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=KGjCxGS1MT0:jWFZUzgW6WY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/KGjCxGS1MT0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/KGjCxGS1MT0/s-e-ooooh-that-how-you-do-it.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/09/s-e-ooooh-that-how-you-do-it.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-1392916636983281727</guid><pubDate>Wed, 08 Oct 2008 16:01:00 +0000</pubDate><atom:updated>2008-10-08T12:01:00.174-04:00</atom:updated><title>Wow - Thanks, Microsoft!</title><description>&lt;p&gt;It's taken me quite a few days to get around to writing this post (been busy preparing and delivering a new ASP.NET MVC presentation at the &lt;a href="http://richmondcodecamp.org/default.aspx" target="_blank"&gt;Richmond Code Camp&lt;/a&gt; last Saturday), but I received a great email last Wednesday welcoming me to a great community of Microsoft professionals, and beginning with this header:&lt;/p&gt;  &lt;p align="center"&gt;&lt;img height="81" src="http://adminframework.mvpaward.com/images/MVPLogo.jpg" width="430" /&gt;&lt;/p&gt;  &lt;p align="left"&gt;I'm now a Microsoft MVP in ASP.NET!&amp;#160; Needless to say, as soon as I found out, I got crazy excited and began thinking of everyone who was so helpful in achieving this great award.&amp;#160; &lt;/p&gt;  &lt;p align="left"&gt;The first two guys I needed to thank were Aaron Marisi and &lt;a href="http://blogs.infragistics.com/blogs/tsnyder/default.aspx" target="_blank"&gt;Todd Snyder&lt;/a&gt;.&amp;#160;&amp;#160; These two have been - and continue to be - crucial in helping me run the &lt;a href="http://www.njdotnet.net/default.aspx" target="_blank"&gt;NJDOTNET User Group&lt;/a&gt;.&amp;#160; With their help (and it was &lt;em&gt;only possible with their help&lt;/em&gt;!) we have been able to expand this monthly meeting to a meeting &lt;em&gt;every week&lt;/em&gt;, providing exponentially more value to the members of the group.&amp;#160; I am going to make it my mission from here on out to make sure these two guys get all the credit they deserve!&lt;/p&gt;  &lt;p align="left"&gt;In a separate class altogether are &lt;a href="http://dotnettemplar.net/" target="_blank"&gt;Ambrose Little&lt;/a&gt; and &lt;a href="https://mvp.support.microsoft.com/profile=E63A6D35-43D9-45F3-AF0E-3312844B5709" target="_blank"&gt;Jason Beres&lt;/a&gt;.&amp;#160; It is these two guys that had the greatest impact on my professional and community life because it was the leadership, knowledge, and obvious passion that these two MVPs (and I mean that in every sense of the acronym) exhibit on a daily basis that inspired me to become so involved with the development community in the first place.&amp;#160; Were it not for them, I probably would still be sitting in the back of the monthly user group meetings, barely saying a word.&amp;#160; But, after working with them for only a short time, their selfless guidance and inspirational example were too much to ignore and I just had to join in and attempt to contribute even half as much as they do.&amp;#160; In fact, I attribute so much of my recent success to them that if I could (and if they didn't already have one!) I would give my award directly to them.&lt;/p&gt;  &lt;p align="left"&gt;I can't even imagine how much more vibrant and exciting our community would be if there were more Aarons, Ambroses, Jasons, and Todds out there pushing things along.&amp;#160; I eternally grateful to be able to work with all of them and even call them my friends.&lt;/p&gt;  &lt;p align="left"&gt;So, thank you everyone for all of your support and I am really looking forward to all of the great community participation in the coming year!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-1392916636983281727?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=cOMbQk4mc1U:StL9lBCqDGI:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=cOMbQk4mc1U:StL9lBCqDGI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=cOMbQk4mc1U:StL9lBCqDGI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=cOMbQk4mc1U:StL9lBCqDGI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/cOMbQk4mc1U" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/cOMbQk4mc1U/wow-thanks-microsoft.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/10/wow-thanks-microsoft.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-5689530083032463181</guid><pubDate>Mon, 27 Oct 2008 18:14:00 +0000</pubDate><atom:updated>2008-10-27T14:14:05.949-04:00</atom:updated><title>Live from PDC: Getting settled in and thoughts on the Keynote</title><description>&lt;p&gt;Greetings from PDC!&lt;/p&gt;  &lt;p&gt;After a long and uncomfortable (for those who don't know me - 6'3&amp;quot;, 220lbs...) flight, I'm finally here in beautiful and smoggy Los Angeles, ready for some PDC action!&amp;#160; First up - this morning's keynote in which Ray Ozzie announced &lt;a href="http://www.microsoft.com/azure/default.mspx" target="_blank"&gt;Windows Azure&lt;/a&gt;, their new offering.&amp;#160; Sounds much like Amazon's service offerings (EC2, S3, etc)... only .NET and a whole slew of other MS offerings.&amp;#160; Sounds pretty darn cool, if you ask me.&amp;#160; So far, I haven't really been interested in utilizing Amazon's services, mostly due to cost, and I wonder how Azure will compare.&amp;#160; Ozzie says their CTP will be free due to changes that may possibly break anything and everything that you make with it at this point (my words, not his), and says that the final pricing will be &amp;quot;competitive with the market&amp;quot; (dunno if that's an exact quote or not, but it's about right).&amp;#160; One has to ask - what's &amp;quot;the market&amp;quot;?&amp;#160; As far as I can tell, that means Amazon's services and - as I just got done saying - their pricing was a deal-breaker for me.&amp;#160; Along these same lines, I noticed that one of the bullet points said &amp;quot;hobbyist-friendly&amp;quot; - with any luck that is referring to their pricing structure.&lt;/p&gt;  &lt;p&gt;I also found it interesting that they referred to &amp;quot;new patterns and best practices such as loosely-coupled services and applications&amp;quot; numerous times...&amp;#160; It really is news to me that loosely-coupled designs are &amp;quot;new patterns and best practices&amp;quot;!&amp;#160;&amp;#160; But, I'm not complaining - if developers are now more or less forced to use loosely coupled architectures to take the most advantage of cloud services, that's a &lt;em&gt;good&lt;/em&gt; thing, right?&lt;/p&gt;  &lt;p&gt;My next session (A Lap Around Windows Azure) is starting, so I should wrap this one up...&amp;#160; but, more to come for sure.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-5689530083032463181?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=q5VbPjjEvjM:O5skyHgTPrw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=q5VbPjjEvjM:O5skyHgTPrw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=q5VbPjjEvjM:O5skyHgTPrw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=q5VbPjjEvjM:O5skyHgTPrw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/q5VbPjjEvjM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/q5VbPjjEvjM/live-from-pdc-getting-settled-in-and_27.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/10/live-from-pdc-getting-settled-in-and_27.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-3283727943169592922</guid><pubDate>Mon, 17 Nov 2008 04:04:00 +0000</pubDate><atom:updated>2008-11-16T23:22:14.525-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Tips and Tricks</category><category domain="http://www.blogger.com/atom/ns#">Test-Driven Development</category><title>Easier Automated Database Testing with SQL Express</title><description>&lt;h3&gt;Scenario&lt;/h3&gt;  &lt;p&gt;I've got a project in which I actually have full create scripts for my database such that I can build a whole new instance from the bottom up.&amp;#160; I've also got some automated unit/integration tests that I want to run against this database, complete with a bunch of scripts that can build some test data for me (unrealistic, I know...&amp;#160; but bear with me :).&amp;#160; Also, &lt;em&gt;I really don't want to have to worry about configuring connection strings&lt;/em&gt; just for my tests - I just want some database available to me when I need it that I can wail on with requests and gets cleaned up for me when I'm done.&amp;#160; Finally, I want to keep my tests as isolated as possible, which to me means a file-based SQL Express database; that way, I can attach, detach, and delete as much as I want with as little exposure and impact to the rest of my build system as possible.&lt;/p&gt;  &lt;h3&gt;Solution&lt;/h3&gt; &lt;p&gt;My solution to the above scenario I found myself in was to create a helper class called TestDatabase whose job is to give me a database when I need one, provide me with a clean version of my test data before each test I run, and clean up after me when I'm done.&amp;#160; To this end, I started searching for how to create a file-based SQL Express database using code, and came up with &lt;a href="http://whereslou.com/2008/10/20/creating-a-sqlexpress-database-file-from-code" target="_blank"&gt;Louis DeJardin's great blog post&lt;/a&gt; that walked me right though it.&amp;#160; After I had that, it was a simple matter of whipping up the class, shown below (Note: this is only a partial listing.&amp;#160; You can get the &lt;a href="http://code.jesschadwick.com/Snippets/TestDatabase.cs" target="_blank"&gt;full listing from my code repository&lt;/a&gt;):&lt;/p&gt; &lt;fieldset&gt;&lt;legend&gt;TestDatabase.cs (partial listing) &lt;/legend&gt; &lt;div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:1a23afb9-a49e-4401-92fa-bbe26c393d43" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#"&gt;public class TestDatabase : IDisposable&lt;br /&gt;{&lt;br /&gt;    private readonly string connectionString;&lt;br /&gt;    private readonly string databaseFilename;&lt;br /&gt;&lt;br /&gt;    public string ConnectionString { get { return connectionString; } }&lt;br /&gt;    public string Schema { get; set; }&lt;br /&gt;    public string TestDataScript { get; set; }&lt;br /&gt;&lt;br /&gt;    public TestDatabase(string databaseFilename, string schema, string testData)&lt;br /&gt;    {&lt;br /&gt;        this.databaseFilename = databaseFilename;&lt;br /&gt;        connectionString = string.Format(&lt;br /&gt;            @"Server=.\SQLEXPRESS; Integrated Security=true;AttachDbFileName={0};",&lt;br /&gt;            Path.GetFullPath(databaseFilename));&lt;br /&gt;        Schema = schema;&lt;br /&gt;        TestDataScript = testData;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void Dispose()&lt;br /&gt;    {&lt;br /&gt;        DeleteDatabaseFiles();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void RecreateTestData()&lt;br /&gt;    {&lt;br /&gt;        EnsureDatabaseCreated();&lt;br /&gt;&lt;br /&gt;        if (!string.IsNullOrEmpty(TestDataScript))&lt;br /&gt;            ExecuteQuery(TestDataScript);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Create a new file-based SQLEXPRESS database&lt;br /&gt;    // (Credit to Louis DeJardin - thanks! http://snurl.com/5nbrc)&lt;br /&gt;    protected void CreateDatabase()&lt;br /&gt;    {&lt;br /&gt;        var databaseName = Path.GetFileNameWithoutExtension(databaseFilename);&lt;br /&gt;&lt;br /&gt;        using (var connection = new SqlConnection(&lt;br /&gt;            "Data Source=.\\sqlexpress;Initial Catalog=tempdb;" +&lt;br /&gt;            "Integrated Security=true;User Instance=True;"))&lt;br /&gt;        {&lt;br /&gt;            connection.Open();&lt;br /&gt;            using (var command = connection.CreateCommand())&lt;br /&gt;            {&lt;br /&gt;                command.CommandText =&lt;br /&gt;                    "CREATE DATABASE " + databaseName +&lt;br /&gt;                    " ON PRIMARY (NAME=" + databaseName +&lt;br /&gt;                    ", FILENAME='" + databaseFilename + "')";&lt;br /&gt;                command.ExecuteNonQuery();&lt;br /&gt;&lt;br /&gt;                command.CommandText =&lt;br /&gt;                    "EXEC sp_detach_db '" + databaseName + "', 'true'";&lt;br /&gt;                command.ExecuteNonQuery();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // After we've created the database, initialize it with any&lt;br /&gt;        // schema we've been given&lt;br /&gt;        if (!string.IsNullOrEmpty(Schema))&lt;br /&gt;            ExecuteQuery(Schema);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/fieldset&gt; &lt;br /&gt;&lt;p&gt;Let's analyze the things we've got going on here:&lt;/p&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;First, we've got the CreateDatabase() method (lines 34-61) - basically ripped right from Louis's blog post linked above - which does the magic of &lt;strong&gt;creating a file-based SQL Express database&lt;/strong&gt;.&amp;#160; It all boils down to a &amp;quot;CREATE DATABASE&amp;quot; and &amp;quot;EXEC sp_detach_db&amp;quot; call on the local SQL Express instance's tempdb database, which everyone has access to.&amp;#160; Then when that's all done, I execute the schema script that the tester passed in to build the database schema and finish the initial setup. &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Now that the database has been created and initialized with its schema, we can run some tests against it!&amp;#160; Problem is, at this point it's just an empty database...&amp;#160; Fortunately for us, we've got the RecreateTestData() method, which just executes the TestDataScript against the current database, allowing us to &lt;strong&gt;easily populate whatever test data&lt;/strong&gt; we want!&amp;#160; This script should include everything it needs to clean out the database and rebuild it from scratch with a new set of clean data. &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;strong&gt;Built-in connection string management.&lt;/strong&gt;&amp;#160; As you can see, our constructor takes in a database filename, builds a connection string out of it, and then exposes that connection string to our testers via a read-only property.&amp;#160; That is one less connection string that our test project has to worry about managing in its app.config (or whatever), which is pretty nice and clean, IMHO! &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Finally, our big finale:&amp;#160; &lt;strong&gt;cleaning up after ourselves&lt;/strong&gt;!&amp;#160; You can see that TestDatabase implements IDisposable, allowing us to create a Dispose() method which cleans up after everything we've done - namely, deleting the database files we've created along the way.&amp;#160; This means that after everything is said and done, we've left not one footprint of our presence on the build system. &lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;Now, after we've got our TestDatabase class available, our unit tests become as easy as this: &lt;/p&gt;&lt;br /&gt;&lt;div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:949df6b2-7296-4552-9d24-63a26f807f2b" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#:nogutter:nocontrols"&gt;public void SomeCoolDatabaseDrivenServiceTest()&lt;br /&gt;{&lt;br /&gt;    var mySchema = System.IO.File.ReadAllText("mySchema.sql");&lt;br /&gt;    var testData = System.IO.File.ReadAllText("testData.sql");&lt;br /&gt;    using (var db = new TestDatabase("TestDatabase.mdf", mySchema, testData))&lt;br /&gt;    {&lt;br /&gt;        db.Initialize();&lt;br /&gt;        var service = new MyService(db.ConnectionString);&lt;br /&gt;        service.DoSomething();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;Of course, individual tests can have even less code if you manage the test database outside of the test by using your test framework's setup and teardown methods.&amp;#160; For example, if I had a whole slew of tests against the same database (which is usually always the case), the test class would start out like this: &lt;/p&gt;&lt;br /&gt;&lt;div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:a7119222-0976-4ef1-bea8-bcf55bf70138" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#:nogutter:nocontrols"&gt;TestDatabase database;&lt;br /&gt;&lt;br /&gt;public void ClassInitialize()&lt;br /&gt;{&lt;br /&gt;    var mySchema = System.IO.File.ReadAllText("mySchema.sql");&lt;br /&gt;    var testData = System.IO.File.ReadAllText("testData.sql");&lt;br /&gt;    database = new TestDatabase("TestDatabase.mdf", mySchema, testData);&lt;br /&gt;    database.Initialize(true);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void TestInitialize()&lt;br /&gt;{&lt;br /&gt;    // Rebuild the test data from scratch before EVERY test&lt;br /&gt;    database.RecreateTestData();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void ClassCleanup()&lt;br /&gt;{&lt;br /&gt;    database.Dispose();&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;Now that we have all of that setup and teardown logic out of the way, we can focus on what we're actually testing, so then that test I showed you before becomes a simple one-liner (as it would have been if we were just passing in a connection string from a configuration file): &lt;/p&gt;&lt;br /&gt;&lt;div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:3df0e1e2-95c0-4fc9-8979-2c76036cc0e0" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#:nogutter:nocontrols"&gt;public void SomeCoolDatabaseDrivenServiceTest()&lt;br /&gt;{&lt;br /&gt;    // No TestDatabase setup - just use its connection string!&lt;br /&gt;    var service = new MyService(database.ConnectionString);&lt;br /&gt;    service.DoSomething();&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;What's cool about this is that not only do we not have to worry about where to get our connection string from, our entire suite of test data is also being rebuilt for us before &lt;em&gt;every &lt;/em&gt;test is run!&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Try It Out For Yourself!&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;If you like what you've seen in this post and want to try it out for yourself, you can grab the full source file (complete with in-line comments and unit tests) from my repository: &lt;a href="http://code.jesschadwick.com/Snippets/TestDatabase.cs" target="_blank" rel="nofollow"&gt;TestDatabase.cs&lt;/a&gt;.&amp;#160; Just drop it in your project and start using it! &lt;br /&gt;&lt;em&gt;Note: The full source file has unit tests included. If you don't want them, you can simply delete them without affecting the main class.&lt;/em&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;As always, I'd love to hear your comments and feedback on all this.&amp;#160; If you've found this useful or - better yet - if you have a better way of doing it, please let me know!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-3283727943169592922?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=xnHm7zj5goo:2GFIAlIPGoc:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=xnHm7zj5goo:2GFIAlIPGoc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=xnHm7zj5goo:2GFIAlIPGoc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=xnHm7zj5goo:2GFIAlIPGoc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/xnHm7zj5goo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/xnHm7zj5goo/easier-automated-database-testing-with.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/11/easier-automated-database-testing-with.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-3909642702062731903</guid><pubDate>Tue, 09 Dec 2008 07:35:00 +0000</pubDate><atom:updated>2008-12-09T02:35:17.269-05:00</atom:updated><title>Princeton, NJ -- Community Events This Week</title><description>&lt;p&gt;If you're in the Princeton, NJ area - or can somehow manage to get here - this week, boy do we have some events for you!&amp;#160; Infragistics has teamed up with our local .NET User Group (NJDOTNET) and our local Microsoft Developer Evangelists out of New York City to bring you the following awesome events:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;strong&gt;Thursday, December 11th, 1PM - 5PM:&amp;#160; &lt;a href="http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032395688&amp;amp;culture=en-US" target="_blank"&gt;MSDN Roadshow&lt;/a&gt;         &lt;br /&gt;&lt;/strong&gt;&lt;em&gt;Presenters:&amp;#160; &lt;/em&gt;Peter Laudati and Bill Zack       &lt;br /&gt;&lt;em&gt;Session 1: Cloud Computing&lt;/em&gt;       &lt;p&gt;Hear about key problems that cloud computing is solving and how these services fit into the Microsoft cloud computing initiatives. Learn about the pillars of the platform, its service lifecycle, and see how they fit with both Microsoft and non-Microsoft technologies.&lt;/p&gt;      &lt;p&gt;&lt;em&gt;Session 2: Silverlight 2 Application Development&lt;/em&gt;&lt;/p&gt;      &lt;p&gt;Now it&amp;#8217;s time for you to get ready to build next generation rich Internet applications with Silverlight!&amp;#160; In this session, we&amp;#8217;ll quickly review what the Silverlight 2 platform is, and then move into the bits and bytes.        &lt;br /&gt;        &lt;br /&gt;If you're interested, register using this link:&lt;/p&gt;      &lt;p&gt;&lt;a title="http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032395688&amp;amp;culture=en-US" href="http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032395688&amp;amp;culture=en-US"&gt;http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032395688&amp;amp;culture=en-US&lt;/a&gt;&lt;/p&gt;      &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Thursday, December 11th, 6:15-8:30 PM:&amp;#160; &lt;a href="http://www.njdotnet.net/default.aspx" target="_blank"&gt;NJDOTNET User Group&lt;/a&gt;&lt;/strong&gt; monthly meeting       &lt;br /&gt;&lt;em&gt;Topic&lt;/em&gt;:&amp;#160; The ASP.NET MVC Framework       &lt;br /&gt;&lt;em&gt;Presenter:&lt;/em&gt;&amp;#160; Jess Chadwick       &lt;p&gt;The Model-View-Controller (MVC) architectural pattern is one that many developers are familiar with which separates an application into three main components: the model, the view, and the controller. The ASP.NET MVC framework provides an alternative to the ASP.NET Web Forms pattern for creating MVC-based Web applications. It is a lightweight, highly testable framework that allows you to easily incorporate this pattern into your applications, helping to separate the different aspects&amp;#160; (input logic, business logic, and UI logic) into loosely-coupled and pluggable elements.        &lt;br /&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Saturday, December 13th,&amp;#160; 9:30 AM - 5:00 PM:&amp;#160; &lt;a href="http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032397108&amp;amp;culture=en-US" target="_blank"&gt;NJDOTNET ASP.NET MVC Firestarter&lt;/a&gt;         &lt;br /&gt;&lt;/strong&gt;Join us at the Infragistics HQ to participate in an all-day, deep-dive into one of Microsoft's latest (and greatest!) technologies: ASP.NET MVC.       &lt;br /&gt;      &lt;p&gt;At the &lt;a href="http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032397108&amp;amp;culture=en-US"&gt;ASP.NET MVC Firestarter&lt;/a&gt;, we&amp;#8217;ll give you a quick tour of the framework, then peel back the layers and dive deeper into how it works.&amp;#160;&amp;#160; As part of that, we&amp;#8217;ll spend time discussing the design and development practices that lead to the creation of the MVC framework.&amp;#160; By the time you leave, you&amp;#8217;ll have enough knowledge to get fired up and start building web applications with it.&lt;/p&gt;      &lt;p&gt;Detailed Agenda:&lt;/p&gt;      &lt;ul&gt;       &lt;li&gt;ASP.NET MVC Introduction          &lt;ul&gt;           &lt;li&gt;The MVC Design Pattern &lt;/li&gt;            &lt;li&gt;Hello World Demo &amp;#8211; Walking through routing, controllers, and views &lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;        &lt;li&gt;Framework Fundamentals &amp;amp; Practices          &lt;ul&gt;           &lt;li&gt;C# 3.0 Primer &lt;/li&gt;            &lt;li&gt;Anonymous Classes &lt;/li&gt;            &lt;li&gt;Lambda Expressions &lt;/li&gt;            &lt;li&gt;Extention Methods &lt;/li&gt;            &lt;li&gt;LINQ &lt;/li&gt;            &lt;li&gt;Dependency Injection &lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;        &lt;li&gt;Routing &amp;amp; Controllers          &lt;ul&gt;           &lt;li&gt;Routing 101 &lt;/li&gt;            &lt;li&gt;Controllers &amp;#8211; Actions &amp;amp; ActionResults &lt;/li&gt;            &lt;li&gt;Controllers &amp;amp; TDD &lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;        &lt;li&gt;Rendering Markup          &lt;ul&gt;           &lt;li&gt;Views (using WebForm tools) &lt;/li&gt;            &lt;li&gt;Extensibility with View Engines &lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;        &lt;li&gt;Working with Data          &lt;ul&gt;           &lt;li&gt;Creating &amp;amp; Submitting Forms &lt;/li&gt;            &lt;li&gt;UI Helpers &lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;        &lt;li&gt;Building Rich Web Interfaces          &lt;ul&gt;           &lt;li&gt;Applying AJAX Helper extensions &lt;/li&gt;            &lt;li&gt;Walkthrough of ASP.NET AJAX + MVC Extensions &lt;/li&gt;            &lt;li&gt;Enhancing MVC with jQuery &lt;/li&gt;            &lt;li&gt;Action Filters &amp;amp; applying to AJAX &lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;     &lt;/ul&gt;      &lt;p&gt;If you are interested in attending this session, please register using the following link:       &lt;br /&gt;&lt;a href="http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032397108&amp;amp;culture=en-US"&gt;http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032397108&amp;amp;culture=en-US&lt;/a&gt;&lt;/p&gt;   &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;I look forward to seeing you at these great events!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-3909642702062731903?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=1mC2YzL_NJ4:OZ0g9Tpq1rI:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=1mC2YzL_NJ4:OZ0g9Tpq1rI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=1mC2YzL_NJ4:OZ0g9Tpq1rI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=1mC2YzL_NJ4:OZ0g9Tpq1rI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/1mC2YzL_NJ4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/1mC2YzL_NJ4/princeton-nj-community-events-this-week.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/12/princeton-nj-community-events-this-week.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-695650976246415543</guid><pubDate>Fri, 19 Dec 2008 17:17:00 +0000</pubDate><atom:updated>2008-12-19T12:17:09.089-05:00</atom:updated><title>ASP.NET MVC Release: So close you can FEEL it!</title><description>&lt;p&gt;I had a great time reading &lt;a href="http://weblogs.asp.net/scottgu/archive/2008/12/19/asp-net-mvc-design-gallery-and-upcoming-view-improvements-with-the-asp-net-mvc-release-candidate.aspx"&gt;ScottGu's blog post today&lt;/a&gt; discussing all of the cool new features coming in the RC. Some of them (like the View page template &lt;strong&gt;not &lt;/strong&gt;including a code-behind file) really got me excited, but I nearly freaked out when I got to the end and read this: &lt;/p&gt;  &lt;blockquote&gt;We'll be releasing the ASP.NET MVC Release Candidate in January. Our plan is to have that build be ASP.NET MVC V1 API and feature-complete and have zero known bugs. We'll give people a short period to upgrade to it, give it a good tire-kicking, and report any last minute issues they find. We'll then ship the official V1 release shortly after that (so not far off now). &lt;/blockquote&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;ASP.NET MVC v1 is SOOO close! I really feel like this framework has come a long way in its relatively short development lifetime and with all of the recent candy that's been added in the past few releases, I can just tell that it will be a &lt;em&gt;wonderful&lt;/em&gt; platform to develop on for years to come. &lt;/p&gt;  &lt;p&gt;Microsoft... ScottGu (and the rest of the MVC team)... You ROCK. I can not wait to be working in this framework on a daily basis.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-695650976246415543?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=AM0jb_iLGK4:VMVAPx37_z0:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=AM0jb_iLGK4:VMVAPx37_z0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=AM0jb_iLGK4:VMVAPx37_z0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=AM0jb_iLGK4:VMVAPx37_z0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/AM0jb_iLGK4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/AM0jb_iLGK4/aspnet-mvc-release-so-close-you-can.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2008/12/aspnet-mvc-release-so-close-you-can.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-1401965474492767499</guid><pubDate>Tue, 17 Feb 2009 08:10:00 +0000</pubDate><atom:updated>2009-04-19T00:47:55.034-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><category domain="http://www.blogger.com/atom/ns#">Tips and Tricks</category><title>Tag Mappings to the Rescue!</title><description>&lt;p&gt;Our recent project at work lately (and the main reason for my previous two months of blog silence) has been upgrading and re-theming our installation of Community Server.&amp;#160; I’ve written a few posts in the past on the couple of modules and customizations I’ve done for our current site, and this upgrade is no different.&amp;#160; In fact, I’ve had to do more!&amp;#160; The most recent one I did just a few minutes ago just happens to be &lt;strong&gt;not&lt;/strong&gt; Community Server-specific at all, but a regular ol’ ASP.NET trick, so I wanted to write about it first.&amp;#160; You’ll see more about the Community Server-specific customizations I’ve had to do following this post.&lt;/p&gt;  &lt;p&gt;Community Server sites are really just the “Community Server Platform” (which encompasses a whole lotta stuff!), and a customizable theme on top of that platform.&amp;#160; Like any well-made site, CS themes are plain ol’ ASPX pages with a mixture of user and server controls.&amp;#160; This leaves us with themes that have got somewhere between a dozen and a million pages with the following mark-up:&lt;/p&gt; &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:227dfd42-2175-45a8-8fec-120f5b78f8a9" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="xml:nocontrols"&gt;&amp;lt;CSForum:BreadCrumb runat="server" Tag="Div" /&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;… but I don’t like what it’s outputting and I want to override some of its behavior.&amp;#160; Overriding the control’s behavior is easy enough, of course – you just extend the class and throw in an &lt;strong&gt;override&lt;/strong&gt; here and there and you’re all good.&amp;#160; Now we’ve got our new control – &lt;em&gt;Infragistics.CommunityServer.Controls.ForumsBreadCrumb&lt;/em&gt; – and the trick is getting this nice shiny control in place.&amp;#160; Your first thought might be that we’re up for a massive global search n’ replace, right?&amp;#160; Wrong!&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I know I kinda ruined the surprise in the title of this post, but for those of you who skipped over that part, forget the global replace – it’s &lt;a href="http://msdn.microsoft.com/en-us/library/ms164641.aspx"&gt;tag mappings&lt;/a&gt; to the rescue!&amp;#160; Tag mappings allow you to substitute (or &lt;em&gt;map&lt;/em&gt; if you want to get technical) one control for another using a simple &lt;em&gt;web.config&lt;/em&gt; change!&amp;#160; In our case, we’ll do this:&lt;/p&gt;&lt;br /&gt;&lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:9cfd485c-68eb-4e4e-8b89-efbb1878c694" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="xml:nocontrols"&gt;&amp;lt;pages&amp;gt;&lt;br /&gt;  &amp;lt;tagMapping&amp;gt;&lt;br /&gt;    &amp;lt;add &lt;br /&gt;  tagType="CommunityServer.Discussions.Controls.BreadCrumb" &lt;br /&gt;  mappedTagType="Infragistics.CommunityServer.Controls.ForumsBreadCrumb"/&amp;gt;&lt;br /&gt;  &amp;lt;/tagMapping&amp;gt;&lt;br /&gt;&amp;lt;/pages&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;It’s pretty straight-forward – you’re telling ASP.NET that everywhere you’ve used a tag in your markup (&lt;em&gt;tagType&lt;/em&gt;) you want to use another type instead (&lt;em&gt;mappedTagType&lt;/em&gt;).&amp;#160; This makes it really easy to override and/or extend the functionality of a control and use your custom version instead of the original, &lt;em&gt;without changing any of your code&lt;/em&gt;!&amp;#160; &lt;/p&gt;&lt;br /&gt;&lt;p&gt;This tactic can really help you reduce the risk of such a major change, since - I don’t know about you - but my history with global replacements in markup pages have more often than not cost me more problems (and time spent fixing those problems) than if I had just manually made all the replacements to begin with.&amp;#160; Next time you’re tempted to do a global replace on a control, take a couple of seconds to think about whether or not this tactic will work for you.&amp;#160; It might end up saving you quite a bit of time!&lt;/p&gt;&lt;br /&gt;&lt;p&gt;And, as always, happy coding!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-1401965474492767499?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=oaCzEgWbsiI:FTX8aPSrgVU:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=oaCzEgWbsiI:FTX8aPSrgVU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=oaCzEgWbsiI:FTX8aPSrgVU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=oaCzEgWbsiI:FTX8aPSrgVU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/oaCzEgWbsiI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/oaCzEgWbsiI/tag-mappings-to-rescue.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/02/tag-mappings-to-rescue.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-1712842956731739626</guid><pubDate>Tue, 10 Mar 2009 18:28:00 +0000</pubDate><atom:updated>2009-03-10T14:28:34.146-04:00</atom:updated><title>Come Learn Silverlight 2 From a Master!</title><description>&lt;p&gt;Hey all - if you're in the Princeton, NJ area this coming Thursday (March 12th, 2009), be sure to stop by our monthly NJDOTNET User Group meeting because this month we will be hosting Jason Beres - international conference speaker, Microsoft MVP, and an author of the &lt;strong&gt;two&lt;/strong&gt; Silverlight Programmers References from WROX Press!&amp;#160; Jason is an incredible speaker and I strongly encourage you to do everything you can to attend this meeting!&amp;#160; Here are the details:&lt;/p&gt;  &lt;p&gt;&lt;i&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p&gt;&lt;i&gt;What:&amp;#160; &lt;/i&gt;NJDOTNET March Meeting &amp;#8211; Understanding RIA&amp;#8217;s with Silverlight 2&lt;/p&gt;  &lt;p&gt;&lt;i&gt;When:&lt;/i&gt; &lt;b&gt;THIS Thursday&lt;/b&gt;, March 10, 2009&amp;#160; 6:15 PM &amp;#8211; 8:30 PM&lt;/p&gt;  &lt;p&gt;&lt;i&gt;Where:&amp;#160; &lt;/i&gt;Infragistics Corporate HQ&amp;#160; (&lt;a href="http://maps.live.com/default.aspx?v=2&amp;amp;FORM=LMLTCC&amp;amp;cp=40.288385~-74.557498&amp;amp;style=h&amp;amp;lvl=18&amp;amp;tilt=-90&amp;amp;dir=0&amp;amp;alt=-1000&amp;amp;scene=9105189&amp;amp;phx=0&amp;amp;phy=0&amp;amp;phscl=1&amp;amp;sp=Point.qqf6r38sm5db_Infragistics%20HQ____&amp;amp;encType=1" target="_blank"&gt;Click here for directions&lt;/a&gt;)&lt;/p&gt;  &lt;p&gt;&lt;i&gt;Who:&lt;/i&gt;     &lt;br /&gt;Jason Beres is the Director of Product Management for Infragistics, the world&amp;#8217;s leading publisher of presentation layer tools.&amp;#160; Jason is one of the founders of Florida .NET User Groups, he is the founder of the Central New Jersey .NET User Group, he is a Visual Basic .NET MVP, and he is on the INETA Speakers Bureau.&amp;#160; Jason is the author of several books on .NET development, including the recently published Silverlight 2 Programmers Reference from Wrox Press.&amp;#160; Jason is a national and international conference speaker; he is a frequent columnist for several .NET publications, and keeps very active in the .NET community.&lt;/p&gt;  &lt;p&gt;&lt;i&gt;Abstract:     &lt;br /&gt;&lt;/i&gt;&lt;strong&gt;Understanding RIA&amp;#8217;s with Silverlight 2     &lt;br /&gt;&lt;/strong&gt;In this code-focused talk, we&amp;#8217;ll look at the features in Silverlight 2 and how they can help you build better RIA (Rich Internet Application) experiences.&amp;#160; We&amp;#8217;ll look at the Silverlight development experience, how to build a Silverlight application with the new Silverlight 3 features, and how this will help you build rich line-of-business experiences using data binding, animations and media.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-1712842956731739626?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=21ZHR-oVjJs:pUgSJb4uQ14:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=21ZHR-oVjJs:pUgSJb4uQ14:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=21ZHR-oVjJs:pUgSJb4uQ14:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=21ZHR-oVjJs:pUgSJb4uQ14:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/21ZHR-oVjJs" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/21ZHR-oVjJs/come-learn-silverlight-2-from-master.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/03/come-learn-silverlight-2-from-master.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-3042413449366530124</guid><pubDate>Sun, 19 Apr 2009 04:37:00 +0000</pubDate><atom:updated>2009-04-19T00:37:26.972-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><category domain="http://www.blogger.com/atom/ns#">Enterprise Library</category><title>Cleaner Validation with ASP.NET MVC Model Binders &amp; the Enterprise Library Validation Application Block</title><description>&lt;p&gt;I accidentally stumbled across an awesome combination the other day:&amp;#160; using the Enterprise Library Validation Block with ASP.NET MVC.&amp;#160; Though I’ve played around with them a few times in the past, this is the first time I’ve &lt;i&gt;really&lt;/i&gt; started to apply the Validation block in a serious application, and it just so happened to have an ASP.NET MVC website as its client.&amp;#160; My jaw dropped more and more as I started to realize the awesomeness that was unfolding before me…&amp;#160; hopefully this blog post will do the same (or as close as possible) to you!&lt;/p&gt;  &lt;h3&gt;Using the Enterprise Library Validation Block&lt;/h3&gt;  &lt;p&gt;It all started with an innocent enough Model requiring a wee bit of validation that I didn’t feel like hand-writing, so (as usual) I turned to the EntLib library to do it for me.&amp;#160; Applying the &lt;a href="http://msdn.microsoft.com/en-us/library/cc309320.aspx" target="_blank"&gt;Enterprise Library Validation Block&lt;/a&gt; was surprisingly simple.&amp;#160; &lt;/p&gt;  &lt;p&gt;It all started with a simple enough class (the names have been changed to protect the innocent):    &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:5ec7780d-89aa-4f7d-8883-e7136fbd5d73" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#:nocontrols"&gt;public class Product&lt;br /&gt;{&lt;br /&gt;    public int ID { get; set; }&lt;br /&gt;    public string Name { get; set; }&lt;br /&gt;    public string Description { get; set; }&lt;br /&gt;    public double Price { get; set; }&lt;br /&gt;    public int Quantity { get; set; }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This is basically just a DTO (data transfer object), but this ain’t the Wild West – there are rules, and they need to be followed!&amp;#160; After a few minutes, I’d come up with something like this: &lt;br /&gt;  &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:47998da8-bce5-44ff-a1b8-9485ae5753a2" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#:nocontrols"&gt;using Microsoft.Practices.EnterpriseLibrary.Validation;&lt;br /&gt;using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;&lt;br /&gt;&lt;br /&gt;public class Product&lt;br /&gt;{&lt;br /&gt;    [RangeValidator(&lt;br /&gt;        1, RangeBoundaryType.Inclusive,             /* Lower Bound */&lt;br /&gt;        int.MaxValue, RangeBoundaryType.Inclusive   /* Upper Bound */&lt;br /&gt;    )]&lt;br /&gt;    public int ID { get; set; }&lt;br /&gt;&lt;br /&gt;    // Let's assume that we've got a field length limitation in&lt;br /&gt;    // our database of 500 characters, which we'll check for here&lt;br /&gt;    [StringLengthValidator(&lt;br /&gt;        1, RangeBoundaryType.Inclusive,             /* Lower Bound */&lt;br /&gt;        500, RangeBoundaryType.Inclusive            /* Upper Bound */&lt;br /&gt;    )]&lt;br /&gt;    public string Name { get; set; }&lt;br /&gt;&lt;br /&gt;    // No rules for the description - anything goes!&lt;br /&gt;    public string Description { get; set; }&lt;br /&gt;&lt;br /&gt;    // The Price can be whatever we want, as long as it's positive&lt;br /&gt;    [RangeValidator(0, RangeBoundaryType.Inclusive, double.MaxValue, RangeBoundaryType.Inclusive)]&lt;br /&gt;    public double Price { get; set; }&lt;br /&gt;&lt;br /&gt;    // Same deal with the Quantity - we can never have a negative quantity&lt;br /&gt;    [RangeValidator(0, RangeBoundaryType.Inclusive, int.MaxValue, RangeBoundaryType.Inclusive)]&lt;br /&gt;    public int Quantity { get; set; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    public bool IsValid()&lt;br /&gt;    {&lt;br /&gt;        return Validate().IsValid;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public ValidationResults Validate()&lt;br /&gt;    {&lt;br /&gt;        return Validation.Validate&amp;lt;Product&amp;gt;(this);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;There are a couple of cool things I like about this setup:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;Declarative validation rules&lt;/b&gt;:&amp;#160; These rules are very explicit expression of business logic - there is no “if-else-then”, mumbo-jumbo.&amp;#160; In other words, there isn’t any code to worry about… and no code means no bugs (well, less bugs at least :).&amp;#160; Moreover, if any of these business rules change, it’s very easy to update these attributes without hunting around for that stray line of “if-else” code somewhere.&amp;#160; Lastly, I’ve heard talk of these mystical “business people” who are also able to read and understand simple code; and, if you run into one of these guys/gals they’ll easily be able to verify that you have the rules set properly as well. &lt;/li&gt;&lt;br /&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;All of the validation logic is in one place&lt;/b&gt;:&amp;#160; all consumers of this class need to do is set its properties and &lt;i&gt;ask the object&lt;/i&gt; whether or not it is valid.&amp;#160; There are no stray “if(string.IsNullOrEmpty(product.Name)” scattered through your code, just “if(product.IsValid())”.&amp;#160; I feel like this approach has a decent amount of cohesion.&amp;#160; Granted, it could be a bit more cohesive if we had, say, a separate “ProductValidator”, but this seems like overkill.&amp;#160; Regardless, it was bugging me enough that I actually created a super-class to encapsulate this logic further of the chain of inheritance and that made me feel a bit more comfortable: &lt;/li&gt;&lt;br /&gt;&lt;br /&gt;  &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:c84976a4-c0ea-4d9e-bb45-9a86217ce7e9" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#"&gt;public class SelfValidatingBase&lt;br /&gt;{&lt;br /&gt;    public bool IsValid()&lt;br /&gt;    {&lt;br /&gt;        return Validate().IsValid;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public ValidationResults Validate()&lt;br /&gt;    {&lt;br /&gt;        return ValidationFactory.CreateValidator(this.GetType())&lt;br /&gt;			.Validate(this);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Product : SelfValidatingBase&lt;br /&gt;{&lt;br /&gt;    // ...&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;As with pretty much anything, there is at least one glaring drawback to this approach:&amp;#160; there is no “real-time” checking.&amp;#160; That is, this approach allows consumers to set invalid values on these validated properties at any time – possibly overwriting valid values without any checks prior to the update.&amp;#160; I think that as long as your application (i.e. developers) know about this limitation, it’s not so much of an issue, at least not for the scenarios I’ve used it in, so this drawback doesn’t really bother me.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now, let’s see how this applies to ASP.NET MVC…&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The Awesomeness that is ASP.NET MVC’s Model Binders&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;When it comes to me and &lt;a href="http://msdn.microsoft.com/en-us/library/dd410405.aspx" target="_blank"&gt;ASP.NET MVC’s Model Binders&lt;/a&gt; it was love at first site – and I haven’t stopped using them since.&amp;#160; In case you’re not sure what I’m talking about, here’s an example.&amp;#160; Instead of an Action with individual parameters and populating a new instance ourselves like this: &lt;br /&gt;&lt;br /&gt;  &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:4e40d84f-cd93-4ff2-8e70-990dd6697595" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#:nocontrols"&gt;[AcceptVerbs(HttpVerbs.Post)]&lt;br /&gt;public ActionResult Create(string username, string message, string userUrl)&lt;br /&gt;{&lt;br /&gt;    var comment = new Comment&lt;br /&gt;                      {&lt;br /&gt;                          Message = message,&lt;br /&gt;                          Username = username,&lt;br /&gt;                          UserUrl = userUrl,&lt;br /&gt;                          PostedDate = DateTime.Now&lt;br /&gt;                      };&lt;br /&gt;    commentsRepository.Add(comment);&lt;br /&gt;    return RedirectToAction("Index");&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;We let the MVC framework populate a new instance for us, like this: &lt;br /&gt;  &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:3c4f99e9-05b5-4fb3-ae77-1ab7bf034225" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#:nocontrols"&gt;[AcceptVerbs(HttpVerbs.Post)]&lt;br /&gt;public ActionResult Create(Comment comment)&lt;br /&gt;{&lt;br /&gt;    commentsRepository.Add(comment);&lt;br /&gt;    return RedirectToAction("Index");&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I just think that’s beautiful, and so I’ve come to (over?)use Model Binders on my Controller Actions almost exclusively.&amp;#160; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;ASP.NET MVC Model Binders + Enterprise Library Validation Block = BFF&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The magic that I refer to at the beginning of this post first exposed itself when I inadvertently used one of my Model objects like the one I showed earlier as an Action parameter (which was really only a matter of time given the fact that I’d taken to using them so much!) using MVC’s Model Binding, and then created some validation logic for it (if you’re not sure what I’m referring to in regards to “creating validation logic”, you’ll want to check out &lt;a href="http://msdn.microsoft.com/en-us/library/dd410404.aspx" target="_blank"&gt;this article on MSDN&lt;/a&gt; before continuing).&amp;#160; As I started writing my validation logic in my Action and populating the ModelState with my validation errors like so: &lt;br /&gt;&lt;br /&gt;  &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:31533d34-4b98-40fe-9c0b-be4658c5c924" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#:nocontrols"&gt;[AcceptVerbs(HttpVerbs.Post)]&lt;br /&gt;public ActionResult Create(Product product)&lt;br /&gt;{&lt;br /&gt;    if (!product.IsValid())&lt;br /&gt;    {&lt;br /&gt;        if(string.IsNullOrEmpty(product.Name))&lt;br /&gt;            this.ModelState.AddModelError("name", "Please enter a product name");&lt;br /&gt;        if(product.Price &amp;lt; 0)&lt;br /&gt;            this.ModelState.AddModelError("price", "Price must be greater than 0");&lt;br /&gt;        if(product.Quantity &amp;lt; 0)&lt;br /&gt;            this.ModelState.AddModelError("quantity", "Quantity must be greater than 0");&lt;br /&gt;&lt;br /&gt;        return View(product);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;	productRepository.Add(product);&lt;br /&gt;    return View("Index");&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now, even if I moved this code outside of my Action, I’d still be pretty embarrassed of it…&amp;#160; but after looking at it for a while I realized that I don’t have to do this after all – the EntLib ValidationResult (usually) maps perfectly to MVC’s Model Binding…&amp;#160; and ModelState errors!&amp;#160; Check out the same code, taking advantage of the EntLib validation results: &lt;br /&gt;  &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:60723655-c705-4ab8-9d7e-23db69cf9837" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#"&gt;[AcceptVerbs(HttpVerbs.Post)]&lt;br /&gt;public ActionResult Create(Product product)&lt;br /&gt;{&lt;br /&gt;    var validationResult = product.Validate();&lt;br /&gt;    if (!validationResult.IsValid)&lt;br /&gt;    {&lt;br /&gt;        foreach (var result in validationResult)&lt;br /&gt;            this.ModelState.AddModelError(result.Key, result.Message);&lt;br /&gt;&lt;br /&gt;        return View(product);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;	productRepository.Add(product);&lt;br /&gt;    return View("Index");&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I added this and awesomeness ensued.&amp;#160; The magic comes from the fact that the &lt;i&gt;Key&lt;/i&gt; field of the EntLib &lt;i&gt;ValidationResult&lt;/i&gt; is the name of the property which is causing the validation error.&amp;#160; This leads to what I can do in line 8 above, which is just iterate through all of the validation errors and add their message to the ModelState using their &lt;i&gt;Key&lt;/i&gt; property, which corresponds to the form id’s that we’re using to populate the model.&amp;#160; Just so you don’t think I’m lying, here’s what the form would look like: &lt;br /&gt;&lt;br /&gt;  &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:77a2dcff-0e86-4551-977b-4be35ad6d950" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#:nocontrols"&gt;&amp;lt;%= Html.ValidationSummary(&lt;br /&gt;		"Create was unsuccessful. &lt;br /&gt;		 Please correct the errors and try again.") %&amp;gt;&lt;br /&gt;&amp;lt;% using (Html.BeginForm()) {%&amp;gt;&lt;br /&gt;    &amp;lt;fieldset&amp;gt;&lt;br /&gt;        &amp;lt;legend&amp;gt;Add New Product&amp;lt;/legend&amp;gt;&lt;br /&gt;        &amp;lt;p&amp;gt;&lt;br /&gt;            &amp;lt;label for="Name"&amp;gt;Name:&amp;lt;/label&amp;gt;&lt;br /&gt;            &amp;lt;%= Html.TextBox("Name") %&amp;gt;&lt;br /&gt;            &amp;lt;%= Html.ValidationMessage("Name", "*") %&amp;gt;&lt;br /&gt;        &amp;lt;/p&amp;gt;&lt;br /&gt;        &amp;lt;p&amp;gt;&lt;br /&gt;            &amp;lt;label for="Description"&amp;gt;Description:&amp;lt;/label&amp;gt;&lt;br /&gt;            &amp;lt;%= Html.TextBox("Description") %&amp;gt;&lt;br /&gt;            &amp;lt;%= Html.ValidationMessage("Description", "*") %&amp;gt;&lt;br /&gt;        &amp;lt;/p&amp;gt;&lt;br /&gt;        &amp;lt;p&amp;gt;&lt;br /&gt;            &amp;lt;label for="Price"&amp;gt;Price:&amp;lt;/label&amp;gt;&lt;br /&gt;            &amp;lt;%= Html.TextBox("Price") %&amp;gt;&lt;br /&gt;            &amp;lt;%= Html.ValidationMessage("Price", "*") %&amp;gt;&lt;br /&gt;        &amp;lt;/p&amp;gt;&lt;br /&gt;        &amp;lt;p&amp;gt;&lt;br /&gt;            &amp;lt;label for="Quantity"&amp;gt;Quantity:&amp;lt;/label&amp;gt;&lt;br /&gt;            &amp;lt;%= Html.TextBox("Quantity") %&amp;gt;&lt;br /&gt;            &amp;lt;%= Html.ValidationMessage("Quantity", "*") %&amp;gt;&lt;br /&gt;        &amp;lt;/p&amp;gt;&lt;br /&gt;        &amp;lt;p&amp;gt;&lt;br /&gt;            &amp;lt;input type="submit" value="Create" /&amp;gt;&lt;br /&gt;        &amp;lt;/p&amp;gt;&lt;br /&gt;    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;&amp;lt;% } %&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;I Think We Can Do Just a Bit Better…&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;So, there you have it – easy validation using ASP.NET MVC Model Binders, MVC’s Validation components, and Enterprise Library’s Validation block.&amp;#160; The preceeding should work like a charm, but me being the perpetual perfectionist and idealist saw one more piece of duplication that I wanted to remove.&amp;#160; Namely, the &lt;i&gt;foreach&lt;/i&gt; loop used to map the &lt;i&gt;ValidationResults&lt;/i&gt; to the &lt;i&gt;ModelState&lt;/i&gt;.&amp;#160; Using an extension method to extend the &lt;i&gt;ValidationResults&lt;/i&gt; class, this duplication can easily be removed like so: &lt;br /&gt;&lt;br /&gt;  &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:477976d8-c7bd-401a-b2ef-ae2e1626d779" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#"&gt;using System.Web.Mvc;&lt;br /&gt;using Microsoft.Practices.EnterpriseLibrary.Validation;&lt;br /&gt;&lt;br /&gt;public static class EntLibValidationExtensions&lt;br /&gt;{&lt;br /&gt;    public static void CopyToModelState(this ValidationResults results, ModelStateDictionary modelState)&lt;br /&gt;    {&lt;br /&gt;        foreach (var result in results)&lt;br /&gt;            modelState.AddModelError(result.Key ?? "_FORM", result.Message);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now the previous Action looks just a bit cleaner: &lt;br /&gt;  &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:db573bfc-5e79-4ee7-b993-20721a89be8b" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="c#"&gt;[AcceptVerbs(HttpVerbs.Post)]&lt;br /&gt;public ActionResult Create(Product product)&lt;br /&gt;{&lt;br /&gt;    var validationResult = product.Validate();&lt;br /&gt;    if (!validationResult.IsValid)&lt;br /&gt;    {&lt;br /&gt;        validationResult.CopyToModelState(this.ModelState);&lt;br /&gt;        return View(product);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    productRepository.Add(product);&lt;br /&gt;    return View("Index");&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;And with that, I’m happy…&amp;#160; What do you think??&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-3042413449366530124?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=VVEcWUkmua8:6Eb9QKngDdc:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=VVEcWUkmua8:6Eb9QKngDdc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=VVEcWUkmua8:6Eb9QKngDdc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=VVEcWUkmua8:6Eb9QKngDdc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/VVEcWUkmua8" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/VVEcWUkmua8/cleaner-validation-with-aspnet-mvc.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/04/cleaner-validation-with-aspnet-mvc.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-9111758740802890470</guid><pubDate>Sun, 19 Apr 2009 15:53:00 +0000</pubDate><atom:updated>2009-04-19T11:53:00.147-04:00</atom:updated><title>Of HTTP Headers and Firefox Add-Ons…</title><description>&lt;p&gt;Until just last week we had hosted our main website on a single web server, however an impressive team effort of just a few days brought that situation to a screeching halt when we finally moved everything to a brand new, long-awaited web farm. The benefits of web farms are both well-known and mostly obvious, but it’s not all roses and puppy dogs - web farms certainly bring their share of headaches as well.&lt;/p&gt;  &lt;p&gt;One of the pains I immediately felt as soon as we went live was the fact that I had no idea &lt;i&gt;which&lt;/i&gt; server in the farm I was hitting with any given request. Yes, of course to you the site user this really shouldn’t (and doesn’t) matter, but for me and my team when something isn’t going quite right, knowing which machine this uninvited behavior is originating from is a crucial piece of information. My knee-jerk reaction was to place some kind of identifier in the page content. To this end, I opened up the master pages on each server, scrolled to the end and plopped in an HTML comment “&amp;lt;!-- A --&amp;gt;”, “&amp;lt;!-- B --&amp;gt;”, and so on. This certainly fit my immediate requirement, but had a few annoying downsides: first off, this certainly wasn’t a maintainable solution, since it would have to be re-applied (probably manually, on &lt;i&gt;every&lt;/i&gt; server) with every release and – if that weren’t enough by itself (which it was!) – it was just plain annoying having to right-click, View Source, scroll to the bottom, and search for this identifier. My immediate need was filled, but the geek in me was raging – there had to be a better way!&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Take 2:&lt;/b&gt; After a bit of consideration, I thought taking advantage of the Footers feature in IIS might be a good idea. I’d never used them before, and didn’t know how well it would work for our goals, but decided to try it out anyway. “Open IIS Management console, right-click on my site, Properties… wait – the HTTP Headers tab!” Most any web developer who’s done a decent bit of AJAX has probably used – or at least seen usage of – custom HTTP Headers as a way of communicating meta-data about web responses (e.g. the “X-MicrosoftAjax” header used in ASP.NET AJAX to identify an AJAX request). Frustrated with myself that I hadn’t thought about this initially, I realized this is exactly where I’d wanted to put this info all along. My initial solution of putting the HTML comment in the text left a bad taste in my mouth for so many reasons, but at this point I realized that the worst part about it was that I was putting &lt;i&gt;response metadata&lt;/i&gt; in the &lt;i&gt;content&lt;/i&gt;, thus committing a blatant violation of Separating Concerns. After slapping my wrist with a ruler and vowing never to do that again (yeah, right!), I set forth to correct it, quickly adding a new custom HTTP Header called “X-ResponseOrigin” on each of my servers, and each with its own unique value. After going back and removing those silly HTML comments, I sat back, made a few requests and happily viewed the responses coming back in Firebug, knowing exactly which server had produced each one.&lt;/p&gt;  &lt;p&gt;So at this point I’m pretty pleased with the way things are going. With Firebug open, I could see everything I needed to in order to troubleshoot server-specific issues. But, after a short time I started getting annoyed again. With every new request, I’d have to scroll down to the bottom of the request history in Firebug, find my request, expand it out, and scroll down, all the while scanning for my new special header. Trying to find this new piece of metadata I added – while arguably better than the previous “View Source, Scroll ‘n Scan” method – was still pretty darn annoying… oh, and what about the guys on my team who &lt;i&gt;didn’t&lt;/i&gt; &lt;i&gt;have&lt;/i&gt; Firebug?? (NOTE: this is really a hypothetical situation, since it is a well-known team rule that not having Firebug on your machine - even if you’re a non-developer – is punishable by 30 push-ups and having to buy everyone donuts the following morning.) It’s at this point that I remembered what Firebug was to begin with – a Firefox Addon – and realized what I must do next…&lt;/p&gt;  &lt;h4&gt;Enter the Firefox Addon&lt;/h4&gt;  &lt;p&gt;I needed to make a custom Firefox Addon to show me what server I was on, right there in my Firefox statusbar. No, it wasn’t a requirement for this project – it was utterly imperative to fulfilling my role of “Lead Code Monkey”; it was my destiny.&lt;/p&gt;  &lt;p&gt;A quick Googling brought me to the Mozilla Developer portal, with its wealth of knowledge of all things Mozilla. Come to find out, writing a Firefox Addon is as simple as some XHTML/XML-ish markup and accompanying JavaScript – what could be easier!? The markup was quick – I just had to declare that I wanted a custom &lt;b&gt;statusbarpanel&lt;/b&gt; that I could put my content in. The resulting code looked like this: &lt;/p&gt;  &lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:9851f8ee-99b6-417e-8501-4239f85e4490" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="xml"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;?xml-stylesheet href="chrome://whereami/skin/overlay.css" type="text/css"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE overlay SYSTEM "chrome://whereami/locale/whereami.dtd"&amp;gt;&lt;br /&gt;&amp;lt;overlay id="whereami-overlay"&lt;br /&gt;         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"&amp;gt;&lt;br /&gt;  &amp;lt;script src="overlay.js"/&amp;gt;&lt;br /&gt;  &amp;lt;stringbundleset id="stringbundleset"&amp;gt;&lt;br /&gt;    &amp;lt;stringbundle id="whereami-strings" src="chrome://whereami/locale/whereami.properties"/&amp;gt;&lt;br /&gt;  &amp;lt;/stringbundleset&amp;gt;&lt;br /&gt;  &amp;lt;statusbar id="status-bar"&amp;gt;&lt;br /&gt;     &amp;lt;statusbarpanel id="whereami.location" label="" /&amp;gt;&lt;br /&gt;  &amp;lt;/statusbar&amp;gt;&lt;br /&gt;&amp;lt;/overlay&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;As you can see, it’s pretty straightforward. We’ve got our standard XML cruft, followed by what looks like a typical JavaScript reference tag, pointing to our script file (which we’ll get to in just a minute). This is followed by our &lt;i&gt;&amp;lt;statusbar&amp;gt;&lt;/i&gt; and &lt;i&gt;&amp;lt;statusbarpanel&amp;gt;&lt;/i&gt; element, letting Firefox know that all we’re going to need for our UI is a little piece of the existing status bar. We also gave that statusbarpanel an ID so we can easily reference it from our scripts later on. Actually, forget “later on” – let’s go see what those scripts look like!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Before we see the code behind all this, let’s revisit the requirements. Basically, we’re look for this add-on to do two things: get our custom HTTP header from requests that come in, and (if we find one) display it in the browser’s status bar. Initially, I had expected the latter to be the difficult part, but much to my surprise it turned out to be as simple as this one line:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;i&gt;document.getElementById(‘whereami.location’).label = responseOrigin;&lt;/i&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The cool part here is the usage of the “document.getElementById()” DOM searching that you’re already used to from your normal JavaScript forays. And, there’s that element ID “whereami.location” that we set earlier in our overlay.xul file. Now that we’ve got the statusbar update figured out, let’s populate the value of &lt;i&gt;responseOrigin&lt;/i&gt;…&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Quick Intro to the XPCOM API&lt;/h5&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;In order to get a chance to look at what the user is browsing, you’ve got to create a class that you can register with Firefox as a listener. From that point, you’ll be notified whenever a browsing event happens – say, when a new response has been received. Before we look at the actual implementation, let’s look at a basic implementation of a Firefox listener class (as taken from the &lt;a href="https://developer.mozilla.org/En/NsIObserver" target="_blank"&gt;Mozilla Developer Center&lt;/a&gt;): &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:d7be77a6-8dbf-417b-83e6-16c4961ced4a" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="js"&gt;function myObserver()&lt;br /&gt;{&lt;br /&gt;  this.register();&lt;br /&gt;}&lt;br /&gt;myObserver.prototype = {&lt;br /&gt;  observe: function(subject, topic, data) {&lt;br /&gt;     // Do your stuff here.&lt;br /&gt;  },&lt;br /&gt;  register: function() {&lt;br /&gt;    var observerService = Components.classes["@mozilla.org/observer-service;1"]&lt;br /&gt;                          .getService(Components.interfaces.nsIObserverService);&lt;br /&gt;    observerService.addObserver(this, "myTopicID", false);&lt;br /&gt;  },&lt;br /&gt;  unregister: function() {&lt;br /&gt;    var observerService = Components.classes["@mozilla.org/observer-service;1"]&lt;br /&gt;                            .getService(Components.interfaces.nsIObserverService);&lt;br /&gt;    observerService.removeObserver(this, "myTopicID");&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You’ll see, all the magic happens in the &lt;i&gt;observe&lt;/i&gt;() method, which gets fired whenever a browser event happens. For the purposes of this app, we’re looking out for any time an &lt;i&gt;http-on-examine-response&lt;/i&gt; event is fired, indicating a new response has been received. That’s pretty easy, we’ll just check the value of &lt;i&gt;eventName&lt;/i&gt; parameter:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;i&gt;if (topic == &amp;quot;http-on-examine-response&amp;quot;) { /* Grab the Response Origin */ }&lt;/i&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now let’s take a look at my implementation: &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:d8a7cb2c-7f69-4067-be74-f9d1391386fe" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="js"&gt;var whereami = {&lt;br /&gt;	requestObserver:&lt;br /&gt;	{&lt;br /&gt;		isRegistered: false,&lt;br /&gt;&lt;br /&gt;	  observe: function(subject, topic, data)&lt;br /&gt;	  {&lt;br /&gt;	    if (topic == "http-on-examine-response") {&lt;br /&gt;		  var statusBar = document.getElementById('whereami.location');&lt;br /&gt;		  statusBar.label = "";&lt;br /&gt;	      var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);&lt;br /&gt;	      var origin = httpChannel.getResponseHeader("X-ResponseOrigin");&lt;br /&gt;		  if(origin &amp;amp;&amp;amp; origin.length &amp;gt; 0)&lt;br /&gt;			origin = "Server: " + origin;&lt;br /&gt;		  statusBar.label = origin;&lt;br /&gt;	    }&lt;br /&gt;	  },&lt;br /&gt;&lt;br /&gt;	  get observerService() {&lt;br /&gt;	    return Components.classes["@mozilla.org/observer-service;1"]&lt;br /&gt;	                     .getService(Components.interfaces.nsIObserverService);&lt;br /&gt;	  },&lt;br /&gt;&lt;br /&gt;	  register: function()&lt;br /&gt;	  {&lt;br /&gt;	    if(this.isRegistered) return;&lt;br /&gt;		&lt;br /&gt;	    this.observerService.addObserver(this, "http-on-examine-response", false);&lt;br /&gt;		this.isRegistered = true;&lt;br /&gt;	  },&lt;br /&gt;&lt;br /&gt;	  unregister: function()&lt;br /&gt;	  {&lt;br /&gt;	    if(!this.isRegistered) return;&lt;br /&gt;		&lt;br /&gt;	    this.observerService.removeObserver(this, "http-on-examine-response");&lt;br /&gt;		this.isRegistered = false;&lt;br /&gt;	  }&lt;br /&gt;	}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;window.addEventListener("load", function(e) { whereami.requestObserver.register(); }, false);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You can see this is basically the same as the snippet from Mozilla, but I’ve added my logic right into the &lt;em&gt;observe()&lt;/em&gt; method (lines 8-16).&amp;#160; Let’s see what we’re doing here:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;First, we have to get a reference to our little section of the statusbar that we reserved via the ID that I gave it earlier in the overlay.xul markup, clearing any existing (stale) data (lines 9 &amp;amp; 10) &lt;/li&gt;&lt;br /&gt;&lt;br /&gt;  &lt;li&gt;Then, we examine the HTTP headers that got sent back in our response, looking for the value of the “X-ResponseOrigin” that was sent from the server.&amp;#160; (lines 11-13) &lt;/li&gt;&lt;br /&gt;&lt;br /&gt;  &lt;li&gt;Finally, we’ll update the statusbar label with the value we got from Step 2&amp;#160; (line 15) &lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-9111758740802890470?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=dFHjZaLhUwc:OXtj04-D2SI:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=dFHjZaLhUwc:OXtj04-D2SI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=dFHjZaLhUwc:OXtj04-D2SI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=dFHjZaLhUwc:OXtj04-D2SI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/dFHjZaLhUwc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/dFHjZaLhUwc/of-http-headers-and-firefox-add-ons.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/04/of-http-headers-and-firefox-add-ons.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-8766725502071182727</guid><pubDate>Sun, 03 May 2009 07:01:00 +0000</pubDate><atom:updated>2009-05-06T22:02:00.399-04:00</atom:updated><title>Leverage ASP.NET Control Adapters for a (slightly) Better UX</title><description>&lt;p&gt;If you’re anything like me, you’ve heard of &lt;a href="http://msdn.microsoft.com/en-us/magazine/cc163543.aspx" target="_blank"&gt;ASP.NET Control Adapters&lt;/a&gt;, but had just dismissed them as a tool that CSS enthusiasts and control freaks could use to make the Web Forms controls render out exactly the way they wanted.&amp;#160; Wanted a &amp;lt;div&amp;gt; instead of a &amp;lt;table&amp;gt; layout? Use a Control Adapter!&amp;#160; Want to… oh, I can’t even come up with a second one.&amp;#160; Point is, until recently I’d basically been dismissing Control Adapters as one of those extension points that the ASP.NET Framework offers, but nobody really has to use to get their usual work done.&amp;#160; Actually, I still pretty much feel that way, but I did recently come with what I think is a pretty good application for a Control Adapter.&amp;#160; I’ll explain it below you let me know what you think!&lt;/p&gt;  &lt;h3&gt;Pet Peeve:&amp;#160; Drop-Downs with a Single Selection&lt;/h3&gt;  &lt;p&gt;Select or Drop-down lists (or “combo boxes” as everyone else calls them) are a pretty useful UI element, so it makes sense that they’re used pretty liberally across the web.&amp;#160; But, have you ever gotten halfway through filling out that form and come across this?&lt;/p&gt;  &lt;p style="padding-left: 20px"&gt;&lt;a href="http://lh3.ggpht.com/_sH3XWh6OzVE/Sf1BNhgffSI/AAAAAAAAAGI/Xl-_1X-sV6E/s1600-h/image%5B5%5D.png"&gt;&lt;img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="41" alt="image" src="http://lh5.ggpht.com/_sH3XWh6OzVE/Sf1BN388miI/AAAAAAAAAGM/CtCeCl3Vn0Y/image_thumb%5B1%5D.png?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Yeah, me too. And it's pretty annoying, especially since they're not usually as evident as this one is and you actually waste time expanding it just to find out that you never had an option to begin with.&amp;#160; The first approach most developers take is to just disable the control, graying it out so it is &amp;quot;clear&amp;quot; to the user that they have no other options to select.&amp;#160; I'm talking about something like this:&lt;/p&gt;  &lt;p style="padding-left: 20px"&gt;&lt;a href="http://lh4.ggpht.com/_sH3XWh6OzVE/Sf1BOeYt0WI/AAAAAAAAAGQ/fXCIBhepSaE/s1600-h/image%5B8%5D.png"&gt;&lt;img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="45" alt="image" src="http://lh3.ggpht.com/_sH3XWh6OzVE/Sf1BO9omHBI/AAAAAAAAAGU/MXfQQWBGmHM/image_thumb%5B2%5D.png?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Meh. It certainly doesn't suck as much as the first example, but it's far from an ideal interaction. Users are still left wondering, &amp;quot;Well, what other options do I have that they won't let me see?&amp;quot; (and - depending upon their level of self-esteem - maybe something like, &amp;quot;What, am I not good enough for those other options? Man, this always happens to me - people are always leaving me out and [...]&amp;quot;). While there's not a whole lot you can do to raise your users' self-esteem (or if there is, that's a whole separate blog post altogether), you can eliminate this whole situation altogether in a very simple and straight-forward way: just &lt;em&gt;tell them&lt;/em&gt; what the value will be. Just do this:&lt;/p&gt;  &lt;p style="padding-left: 20px"&gt;&lt;a href="http://lh4.ggpht.com/_sH3XWh6OzVE/Sf1BPMTCspI/AAAAAAAAAGY/bxY62irrJdo/s1600-h/image%5B11%5D.png"&gt;&lt;img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="39" alt="image" src="http://lh4.ggpht.com/_sH3XWh6OzVE/Sf1BPhZ47GI/AAAAAAAAAGc/qMe1mlbk-zQ/image_thumb%5B3%5D.png?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Looks simple enough, right? I'll bet for the developers in the crowd, your wheels are already churning, trying to figure out the best way to do this. Just like me, your knee-jerk reaction is probably going to involve extending or wrapping &lt;strong&gt;DropDownList&lt;/strong&gt;, but the problem with that is that you now have this new control and in order to use it you have &lt;em&gt;scour your entire site&lt;/em&gt; and replace any instances of &lt;strong&gt;DropDownList&lt;/strong&gt; with &lt;strong&gt;MySuperAwesomeDropDownList&lt;/strong&gt;. But, since that really wasn't an option for me, my response was to create a Control Adapter.&lt;/p&gt;  &lt;h3&gt;Implementing a Custom Control Adapter&lt;/h3&gt;  &lt;p&gt;ASP.NET Control Adapters are a neat way of controlling exactly how controls get rendered down to your clients… even the ASP.NET framework controls!&amp;#160; To take advantage of them, there are two steps: first, create your adapter; then, register it in your .browsers file so that the framework will pick it up.&lt;/p&gt;  &lt;p&gt;To achieve the behavior I showed earlier, what we’re going to want to do is override the way our &lt;strong&gt;DropDownList&lt;/strong&gt; controls get rendered out and insert some logic.&amp;#160; Namely, if we’ve got any more than one item, let the control do its thing… but, if we’ve got only one item, take over and instead just render the text of the item out instead of the combo box.&amp;#160; Here’s the code:&lt;/p&gt;  &lt;div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:dced15da-858d-4a2b-a6d0-8a5cfa23726d" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;   &lt;pre class="c#" name="code"&gt;public class SmartListControlAdapter&lt;br /&gt;    : System.Web.UI.Adapters.ControlAdapter&lt;br /&gt;{&lt;br /&gt;    protected ListControl WrappedControl&lt;br /&gt;    {&lt;br /&gt;        get { return this.Control as ListControl; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected bool ShouldDisplaySmartText&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return WrappedControl.Items.Count &amp;lt; 2&lt;br /&gt;                &amp;amp;&amp;amp; WrappedControl.SelectedItem != null;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected override void Render(System.Web.UI.HtmlTextWriter writer)&lt;br /&gt;    {&lt;br /&gt;        if (ShouldDisplaySmartText)&lt;br /&gt;            writer.Write(smartText());&lt;br /&gt;        else&lt;br /&gt;            base.Render(writer);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private string smartText()&lt;br /&gt;    {&lt;br /&gt;        return string.Format(&amp;quot;&amp;lt;span class='smartListValue'&amp;gt;{0}&amp;lt;/span&amp;gt;&amp;quot;,&lt;br /&gt;                             WrappedControl.SelectedItem.Text);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You’ll see I added the &lt;strong&gt;WrappedControl&lt;/strong&gt; property to cast the base &lt;strong&gt;Control&lt;/strong&gt; property to a &lt;strong&gt;ListControl&lt;/strong&gt; so I don’t have to do that every time I access it.&amp;#160; Wait – why a &lt;strong&gt;ListControl&lt;/strong&gt; when I said earlier that we were targeting a &lt;strong&gt;DropDownList&lt;/strong&gt; control instead?&amp;#160; Well, after I was done writing all the code you see above, ReSharper let me know that based on the way I was using my reference, I was only using those properties and methods defined in the &lt;strong&gt;ListControl&lt;/strong&gt; base class.&amp;#160; Even though I probably won’t ever use this for anything other than the &lt;strong&gt;DropDownList&lt;/strong&gt;, I figured why limit myself? :)&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You’ll also notice that – outside of the casting to a &lt;strong&gt;ListControl&lt;/strong&gt; – that nowhere in this adapter code does it say which control it’s targeting.&amp;#160; In order to actually apply this adapter to the controls on my pages, I’ll need to tell the framework in a separate location which controls I’d like to apply it to.&amp;#160; This is where the &lt;em&gt;.browsers&lt;/em&gt; file(s) come in.&amp;#160; If your project doesn’t have an &lt;strong&gt;App_Browsers&lt;/strong&gt; folder, you can right-click on your project and click &lt;em&gt;Add &amp;gt; Add ASP.NET Folder &amp;gt; App_Browsers&lt;/em&gt;.&amp;#160; Once this is complete, you can again right-click on this new folder and add a new item using the Browser File template (the name, other than .browser, doesn’t matter).&amp;#160; You can then paste the following inside this file:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:cb23b4cb-73d3-471b-955b-d403ffeb48aa" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="xml"&gt;&amp;lt;browsers&amp;gt;&lt;br /&gt;    &amp;lt;browser refID="Default"&amp;gt;&lt;br /&gt;        &amp;lt;controlAdapters&amp;gt;&lt;br /&gt;            &amp;lt;adapter&lt;br /&gt;                controlType="System.Web.UI.WebControls.DropDownList"&lt;br /&gt;                adapterType="ControlAdapters.SmartDropDownListAdapter"&lt;br /&gt;            /&amp;gt;&lt;br /&gt;        &amp;lt;/controlAdapters&amp;gt;&lt;br /&gt;    &amp;lt;/browser&amp;gt;&lt;br /&gt;&amp;lt;/browsers&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Simple enough, right?&amp;#160; Here in the ControlAdapters section for the Default (every) browser, we’re telling the framework to wrap all of our &lt;strong&gt;DropDownList&lt;/strong&gt; instances with our new &lt;strong&gt;SmartDropDownListAdapter&lt;/strong&gt;.&amp;#160; It really doesn’t get much simpler than that!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now we can create a quick test page:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="wlWriterEditableSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:e09985d7-f634-4db3-8123-523ab283578e" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;pre name="code" class="xml"&gt;&amp;lt;p&amp;gt;&lt;br /&gt;    Regular Drop-Down:&lt;br /&gt;    &amp;lt;asp:DropDownList ID="RegularDropDown" runat="server" AutoPostBack="true"&amp;gt;&lt;br /&gt;        &amp;lt;asp:ListItem Text="First" Value="1" /&amp;gt;&lt;br /&gt;        &amp;lt;asp:ListItem Text="Second" Value="2" /&amp;gt;&lt;br /&gt;        &amp;lt;asp:ListItem Text="Third" Value="3" Selected="True" /&amp;gt;&lt;br /&gt;        &amp;lt;asp:ListItem Text="Fourth" Value="4" /&amp;gt;&lt;br /&gt;    &amp;lt;/asp:DropDownList&amp;gt;&lt;br /&gt;    &amp;lt;br /&amp;gt;&lt;br /&gt;    &amp;lt;em&amp;gt;Selected Value:  &amp;lt;%= RegularDropDown.SelectedValue%&amp;gt;&amp;lt;/em&amp;gt;&lt;br /&gt;&amp;lt;/p&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;p&amp;gt;&lt;br /&gt;    Smart Drop-Down:&lt;br /&gt;    &amp;lt;asp:DropDownList ID="SmartDropDown" runat="server"&amp;gt;&lt;br /&gt;        &amp;lt;asp:ListItem Text="One Value" Value="1" /&amp;gt;&lt;br /&gt;    &amp;lt;/asp:DropDownList&amp;gt;&lt;br /&gt;    &amp;lt;br /&amp;gt;&lt;br /&gt;    &amp;lt;em&amp;gt;Selected Value:  &amp;lt;%= SmartDropDown.SelectedValue%&amp;gt;&amp;lt;/em&amp;gt;&lt;br /&gt;&amp;lt;/p&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You can see I added a few test lines that write out the &lt;strong&gt;SelectedValue&lt;/strong&gt; after each of the controls to prove that the underlying &lt;strong&gt;DropDownList&lt;/strong&gt; control is not modified, just displayed differently.&amp;#160; This means that the &lt;strong&gt;SelectedValue&lt;/strong&gt; (along with everything else) can still be used as normal.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Finally, the moment we’ve all been waiting for; the results of the previous snippet:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_sH3XWh6OzVE/Sf1BPyFkiFI/AAAAAAAAAGg/COPwK2bZaDU/s1600-h/image%5B2%5D.png"&gt;&lt;img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="131" alt="image" src="http://lh3.ggpht.com/_sH3XWh6OzVE/Sf1BQQk8isI/AAAAAAAAAGk/bU0bcoY0eBY/image_thumb.png?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Not incredibly styled, but beautiful nonetheless!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Your Thoughts&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;So, what do you think about this approach?&amp;#160; Has this problem bothered you before?&amp;#160; What ways have you solved it?&amp;#160; I’d love to hear about them!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-8766725502071182727?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=jWzeQi8SMMY:gFFhMYlZUDM:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=jWzeQi8SMMY:gFFhMYlZUDM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=jWzeQi8SMMY:gFFhMYlZUDM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=jWzeQi8SMMY:gFFhMYlZUDM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/jWzeQi8SMMY" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/jWzeQi8SMMY/leverage-aspnet-control-adapters-for.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/05/leverage-aspnet-control-adapters-for.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-5669932638341604103</guid><pubDate>Thu, 14 May 2009 03:27:00 +0000</pubDate><atom:updated>2009-05-14T15:20:35.974-04:00</atom:updated><title>Some Tips on How to Ship Better Code</title><description>&lt;p&gt;In my last post, I pontificated about the notion that &lt;a href="http://jesschadwick.blogspot.com/2009/05/real-software-artisans-ship.html"&gt;Real Software Artists Ship&lt;/a&gt;.&amp;#160; But, I’ve got to take a step back and admit something – in that last post, I was full of crap.&amp;#160; I don’t really consider myself an “Agilist”, nor do I come anywhere close to zealousness when it comes to TDD, but I have studied (and I use the term loosely!) these movements for some time now and have been able to adopt many of them into my daily grind with varying degrees of success.&amp;#160; &lt;/p&gt;  &lt;p&gt;Here are a few that I have found to be the most helpful in shipping better code as fast as possible:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;strong&gt;Use Source Control:&lt;/strong&gt;&amp;#160; I originally didn’t have this listed until I just had to come back and put it as #1.&amp;#160; I’m sure you’re already doing this, but I just had to say it anyway.&amp;#160; If you’re not using source control, &lt;a href="http://lh5.ggpht.com/_sH3XWh6OzVE/SguPo95GPnI/AAAAAAAAAGo/Y_JINlTE6vQ/s1600-h/zombie_hamsters1%5B2%5D.jpg" target="_blank"&gt;rabid hamsters&lt;/a&gt; will eat your code and there is nothing you will be able to do about it. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Unit Testing&lt;/strong&gt;:&amp;#160; What always seems to put everyone off about TDD is the seemingly massive amount of additional work it adds and the recommended zealousness with which you should adhere to it. To those complaints I say: obviously it’s more work; nobody’s debating that.&amp;#160; But, the ROI of having a suite of regression tests alone is so incredibly high it’s foolish not to do it. And, if you’re not keen on religiously adhering to a rigid development process of not writing a line of production code that’s not backed by a test, then don’t do it… but do seriously consider writing a least a few tests to cover the &lt;strong&gt;core functionality&lt;/strong&gt; of your code at the very least.&amp;#160; Writing tests after the fact still offers significant value, even if you aren’t enjoying the full suite of benefits that true TDD has to offer. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Continuous Integration (CI) Builds&lt;/strong&gt;:&amp;#160; At my last job, a co-worker of mine had a sticker affixed to his monitor proudly proclaiming, “&lt;a href="http://www.codinghorror.com/blog/archives/000818.html"&gt;It works on my machine&lt;/a&gt;.”&amp;#160; Even if you are a one-developer shop, the benefits of ensuring that you’ve successfully checked in everything needed to build your application are pretty spectacular.&amp;#160; This is so very relevant because the fact is – one-developer shop or not – your production environment is &lt;strong&gt;not&lt;/strong&gt; your machine (or if it is, well, I don’t know what to say… stop doing that? Pretty please?).&amp;#160; Also, if you’ve already got unit tests from the previous recommendation, you’ll find that they go very well with CI Builds.&amp;#160; They go beyond a simple compile to actually running your full suite of unit tests to exercise your code every time, which is a huge win! &lt;a href="http://www.codinghorror.com/blog/archives/000818.html" target="_blank"&gt;&lt;img title="works-on-my-machine-starburst" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; margin-left: 0px; margin-right: 0px; border-right-width: 0px" height="96" alt="works-on-my-machine-starburst" src="http://lh4.ggpht.com/_sH3XWh6OzVE/SguPpZK3xkI/AAAAAAAAAGw/EmGh996ofgk/works-on-my-machine-starburst%5B8%5D.png?imgmax=800" width="100" align="right" border="0" /&gt;&lt;/a&gt;       &lt;blockquote&gt;&lt;strong&gt;Your company doesn't have a CI server? Start one on your machine!&lt;/strong&gt;         &lt;br /&gt;This may sound contrary to avoiding the &amp;quot;Works on my machine&amp;quot; syndrome, but having a continuous integration server - even on your own machine - is better than nothing at all. You may not be testing your code on another machine, but you are at least testing it outside of your working codebase and are still being forced to run your unit tests at regular intervals, which are pretty big wins regardless of which machine they're occurring on.&lt;/blockquote&gt;   &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Use a Refactoring Tool (Liberally):&lt;/strong&gt;&amp;#160; There are some bugs that just never should have happened.&amp;#160; I’m talking about things like existing code that worked until you wanted to move it into its own method – now it’s throwing null reference exceptions because you forgot to initialize that one variable.&amp;#160; Now, I’m not saying that these tools will eliminate this scenario, but they will make it much more difficult to achieve.&amp;#160; Interestingly enough, for those tools like &lt;a href="http://www.jetbrains.com/resharper/download/" target="_blank"&gt;ReSharper&lt;/a&gt; that provide suggestions on improving your code, I found that I was actually &lt;em&gt;learning&lt;/em&gt; some things while using these tools!&amp;#160; At the very least, those suggestions really help encourage you to clean up your code by acting like a nagging parent - “are you really going to leave this like this? This is embarrassing!”&amp;#160; Course, unlike the nagging parent, if you disagree with the suggestion, you can just turn it off! &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Those are my main tips.&amp;#160; What are some of yours?&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-5669932638341604103?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=nSLvQieK668:4bjQWk33BxM:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=nSLvQieK668:4bjQWk33BxM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=nSLvQieK668:4bjQWk33BxM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=nSLvQieK668:4bjQWk33BxM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/nSLvQieK668" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/nSLvQieK668/some-tips-on-how-to-ship-better-code.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/05/some-tips-on-how-to-ship-better-code.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-7774276248364936945</guid><pubDate>Thu, 14 May 2009 04:02:00 +0000</pubDate><atom:updated>2009-05-14T01:52:03.380-04:00</atom:updated><title>Real Software Artisans Ship</title><description>&lt;p&gt;In one of his amazing screenplays, &lt;a href="http://www.imdb.com/title/tt0104348/"&gt;Glengarry Glen Ross&lt;/a&gt;, David Mamet sends in a rock star salesman (played by Alec Baldwin) to antagonize an office of poorly performing salesmen.&amp;#160; He reminds them of a core tenet of sales: “A-B-C: Always Be Closing”.&amp;#160; Otherwise, First prize is a Cadillac El Dorado; Second Prize is a set of steak knives; Third prize is you’re fired.&lt;/p&gt;  &lt;div style="float: right; margin-left: 10px"&gt;&lt;object width="340" height="285"&gt;&lt;param name="movie" value="http://www.youtube.com/v/y-AXTx4PcKI&amp;amp;hl=en&amp;amp;fs=1&amp;amp;rel=0&amp;amp;color1=0x006699&amp;amp;color2=0x54abd6&amp;amp;border=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/y-AXTx4PcKI&amp;amp;hl=en&amp;amp;fs=1&amp;amp;rel=0&amp;amp;color1=0x006699&amp;amp;color2=0x54abd6&amp;amp;border=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="340" height="285"&gt;&lt;/embed&gt;&lt;/object&gt;    &lt;p style="margin-left: 15px; clear: both"&gt;&lt;strong&gt;Glengarry Glen Ross&lt;/strong&gt; (warning: NSFW - language)&lt;/p&gt; &lt;/div&gt;  &lt;p&gt;Steve Jobs says, “real artists ship.”&amp;#160; Now, I’m no artist, but code undoubtedly contains structure and style. When we developers &lt;a href="http://en.wikipedia.org/wiki/Software_Craftsmanship" target="_blank"&gt;care enough about our craft&lt;/a&gt; to consider this structure and style during the course of development, I don’t think it’d be too far off to consider ourselves an artist of sorts.&amp;#160; Or, if you want to sound more original (or pretentious) you might call us “Artisans”.&lt;/p&gt;  &lt;p&gt;Of course, Steve Jobs built a booming hardware and software empire on the motto of “real artists ship,” so I don’t think it’s too far of a stretch to embrace and extend—er, I mean &lt;em&gt;paraphrase&lt;/em&gt; Steve’s great line into, “&lt;strong&gt;real software artisans ship&lt;/strong&gt;.”&amp;#160; &lt;a href="http://en.wikipedia.org/wiki/Agile_software_development" target="_blank"&gt;Agile methodologies&lt;/a&gt; preach similarly: “A-B-S:&amp;#160; Always Be Shipping.”&amp;#160; If you’re practicing Agile properly, you are constantly shipping; you are shipping something at the end of every iteration.&amp;#160; Even if your customers/clients aren’t actually getting their hands on it and using it, you should still be “shipping” it.&amp;#160; You should strive to constantly and consistently have something that works. Test-Driven Development helps a great deal with this because, &lt;a title="This keynote is pretty long, and he says it pretty far in... I&amp;#39;d recommend watching the whole thing regardless." href="http://railsconf.blip.tv/file/2089545/"&gt;as Uncle Bob says&lt;/a&gt;, if you’re practicing it zealously you never go more than a few minutes without everything working.&lt;/p&gt;  &lt;p&gt;Ok, so what about the &lt;em&gt;real world&lt;/em&gt;?&amp;#160; I know, I know – there are plenty of Agile shops and TDD zealots working in the “real world”, but even the Agilists will (regretfully) admit that a majority of the software development industry is simply not following these practices (and some aren’t following any practices at all!).&amp;#160; But, does not being an active Agile practitioner preclude you from constantly and consistently shipping?&amp;#160; &lt;/p&gt;  &lt;p&gt;There is obviously a vast difference between the quick iterations preached by Agile methodologies and a &lt;a href="http://en.wikipedia.org/wiki/Death_march_(software_development)" target="_blank"&gt;Death March&lt;/a&gt;, I’d like to think that even if you or your team are following a Waterfall or SCRUM-fall or even a Free-fall approach that there is still some room for “constantly shipping”.&amp;#160; Sure, you might have to loosen the definition of “constantly” to fit your reality… but it’s doable!&amp;#160; Following – or better yet, adapting – even some of the Agile methodologies is a great first step (for more on that, check out my follow-up post &lt;a href="http://jesschadwick.blogspot.com/2009/05/some-tips-on-how-to-ship-better-code.html" target="_blank"&gt;Some Tips on How to Ship Better Code&lt;/a&gt;).&amp;#160; More importantly, just keeping the goal of a shipping product instead of that next big feature in mind will probably help more than anything.&lt;/p&gt;  &lt;p&gt;What do you think?&amp;#160; Have you been able to effectively employ any techniques in a Waterfall-ish environment to help improve your ability to ship regularly? Is this entire post just full of hot air?&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Note:&amp;#160; This post was heavily influenced by &lt;a href="http://www.infoq.com/presentations/archaeopteryx-bowkett" target="_blank"&gt;Giles Bowkett’s incredibly awesome presentation at RubyFringe&lt;/a&gt;.&amp;#160; You &lt;strong&gt;must, must, must&lt;/strong&gt; watch it!!&lt;/p&gt;&lt;/blockquote&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-7774276248364936945?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=L0jJ-W4H22A:zxIbie6SKoU:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=L0jJ-W4H22A:zxIbie6SKoU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=L0jJ-W4H22A:zxIbie6SKoU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=L0jJ-W4H22A:zxIbie6SKoU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/L0jJ-W4H22A" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/L0jJ-W4H22A/real-software-artisans-ship.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/05/real-software-artisans-ship.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-2734221551840094336</guid><pubDate>Sat, 16 May 2009 17:18:00 +0000</pubDate><atom:updated>2009-05-16T13:22:45.954-04:00</atom:updated><title>Windows 7 Training and Informational Resources</title><description>&lt;p&gt;Microsoft Learning has just launched three free eLearning Clinics that you or your friends and co-workers may be interested in checking out. These Clinics are geared towards three different audiences, and focus on introducing new features and functionality to those interested in simply learning more about the OS or those that are already considering deploying in the near future. &lt;/p&gt;  &lt;p&gt;&lt;img style="display: inline; margin-left: 0px; margin-right: 0px" align="right" src="http://bit.ly/w5Tjv?r=td" /&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://www.microsoft.com/learning/elearning/course/10125.mspx"&gt;What’s New in Windows 7 for Consumers&lt;/a&gt; (1 Hour) &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.microsoftelearning.com/eLearning/gotoResource.aspx?resourceId=38b7b771-a840-4bc6-a518-c8cf79c9a24c&amp;amp;language=en-US&amp;amp;country=US&amp;amp;locale=en-US&amp;amp;style=Learning"&gt;What’s New in Windows 7 for IT Professionals&lt;/a&gt; (2 Hours) &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.microsoftelearning.com/eLearning/gotoResource.aspx?resourceId=0d4cdfd6-b1ad-4a91-899a-7866f498735e&amp;amp;language=en-US&amp;amp;country=US&amp;amp;locale=en-US&amp;amp;style=Learning"&gt;What’s New in Windows 7 for Information Workers&lt;/a&gt; (2 Hours) &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Also, in case you are interested in more Windows 7 training and skills development information, the new &lt;a href="http://www.microsoft.com/learning/windows-7/default.mspx"&gt;Windows 7 Learning Portal&lt;/a&gt; is now live as well! This site is currently showcasing great readiness content, including &lt;a href="http://www.microsoft.com/learning/snacks/default.mspx#WIN7"&gt;7 Silverlight Learning Snacks&lt;/a&gt;, &lt;a href="http://www.microsoft.com/learning/windows-7/default.mspx#BOOKS"&gt;free sample chapters from upcoming MS Press Books&lt;/a&gt;, &lt;a href="http://www.microsoft.com/learning/windows-7/default.mspx#PLANS"&gt;Learning Plans&lt;/a&gt;, &lt;a href="http://www.microsoft.com/learning/windows-7/default.mspx#CLASSROOM"&gt;links to clinics/HOLs&lt;/a&gt; and more. If you care to check it out, click on any of those links or visit the homepage: &lt;b&gt;&lt;/b&gt;&lt;a href="http://www.microsoft.com/learning/windows-7/default.mspx"&gt;http://www.microsoft.com/learning/windows-7/default.mspx&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Enjoy, and let me know if you find anything helpful to you!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-2734221551840094336?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=0I4L1v5y3S0:A9DJO_0ktcE:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=0I4L1v5y3S0:A9DJO_0ktcE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=0I4L1v5y3S0:A9DJO_0ktcE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=0I4L1v5y3S0:A9DJO_0ktcE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/0I4L1v5y3S0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/0I4L1v5y3S0/windows-7-training-and-informational.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/05/windows-7-training-and-informational.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-916857720309146962</guid><pubDate>Thu, 21 May 2009 00:41:00 +0000</pubDate><atom:updated>2009-05-20T21:35:58.255-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><category domain="http://www.blogger.com/atom/ns#">Silverlight</category><title>Helping Silverlight and ASP.NET MVC Work Together</title><description>&lt;p&gt;If you’ve worked with Silverlight you’ve probably used the &lt;a href="http://msdn.microsoft.com/en-us/library/cc838274(VS.95).aspx" target="_blank"&gt;WebForms control that comes with the Silverlight SDK&lt;/a&gt;.&amp;#160; Technically, you can still continue to use this control with ASP.NET MVC, you’ll just need to add the &lt;strong&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb398863.aspx" target="_blank"&gt;ScriptManager&lt;/a&gt;&lt;/strong&gt; with &lt;strong&gt;EnablePartialRendering=”false”&lt;/strong&gt; like so:&lt;/p&gt;  &lt;pre class="xhtml" name="code"&gt;    &amp;lt;form id=&amp;quot;form&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;asp:ScriptManager runat=&amp;quot;server&amp;quot; EnablePartialRendering=&amp;quot;false&amp;quot; /&amp;gt;&lt;br /&gt;        &amp;lt;asp:Silverlight ID=&amp;quot;MySLApp&amp;quot; runat=&amp;quot;server&amp;quot; &lt;br /&gt;            MinimumVersion=&amp;quot;2.0.31005.0&amp;quot;&lt;br /&gt;            Source=&amp;quot;~/ClientBin/MySLApp.xap&amp;quot;&lt;br /&gt;            OnPluginLoaded=&amp;quot;pluginLoaded&amp;quot; &lt;br /&gt;            InitParameters=&amp;quot;myParam=true&amp;quot;&lt;br /&gt;            Width=&amp;quot;415&amp;quot; Height=&amp;quot;280&amp;quot; /&amp;gt;&lt;br /&gt;    &amp;lt;/form&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Sure, this &lt;em&gt;technically&lt;/em&gt; still works, but it's not very MVC-like, is it? The new ASP.NET MVC parlance is filled with code snippets and &lt;a href="http://msdn.microsoft.com/en-us/library/bb383977.aspx" target="_blank"&gt;Extension Methods&lt;/a&gt;, not Server Controls! We'll instead want something that looks like this:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="c-sharp" name="code"&gt;&amp;lt;%= Html.Silverlight(&amp;quot;~/ClientBin/MySLApp.xap&amp;quot;, new Size(415, 280),&lt;br /&gt;                     new {&lt;br /&gt;                            MinimumVersion=&amp;quot;2.0.31005.0&amp;quot;,&lt;br /&gt;                            OnPluginLoaded=&amp;quot;pluginLoaded&amp;quot;, &lt;br /&gt;                            InitParameters=&amp;quot;myParam=true&amp;quot;&lt;br /&gt;                         }) %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;&lt;p&gt;Personally, I think the Extension Method way looks a lot cleaner and feels a lot more natural in MVC Land. However, if you don't really see a difference between those two, or see the difference and don't really care one way or another, feel free to continue using the WebForms example and don't bother reading any further. Just be sure to include that &lt;strong&gt;ScriptManager&lt;/strong&gt;, make sure you set &lt;strong&gt;EnablePartialRendering=&amp;quot;false&amp;quot;&lt;/strong&gt; and you'll be ready to go. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Creating the Extension Methods&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I'm assuming if you're still reading that you not only dig the &lt;strong&gt;Html.Silverlight&lt;/strong&gt; Extension Method above, but you're more interested to see how it works! Well, it's pretty simple, really... &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Before I show you the code, let's take a step back and reevaluate what I'm really looking to do here. Sure, I said before that I wanted to replace the Silverlight WebForms control, but what I really want to do is duplicate the HTML it renders (since that's what it's all about, right?). So, here's the markup I'm shooting for:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="xhtml" name="code"&gt;    &amp;lt;object data=&amp;quot;data:application/x-silverlight-2,&amp;quot; type=&amp;quot;application/x-silverlight-2&amp;quot; height=&amp;quot;280px&amp;quot; width=&amp;quot;415px&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;param name='minRuntimeVersion' value='2.0.31005.0' /&amp;gt;&lt;br /&gt;        &amp;lt;param name='autoUpgrade' value='true' /&amp;gt;&lt;br /&gt;        &amp;lt;param name='source' value='/ClientBin/LogUploader.xap' /&amp;gt;&lt;br /&gt;        &amp;lt;param name='OnPluginLoaded' value='pluginLoaded' /&amp;gt;&lt;br /&gt;        &amp;lt;param name='InitParameters' value='customParam=true' /&amp;gt;&lt;br /&gt;        &amp;lt;!-- [ Silverlight not installed message here ] --&amp;gt;&lt;br /&gt;    &amp;lt;/object&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Pretty straighforward, right? Basically, you've got the &amp;lt;object&amp;gt; tag with some pretty standard attributes, then a bunch of &amp;lt;param&amp;gt; tags inside, filled with name/value pairs. Should be pretty simple to reproduce - let's a shot at it. The way I went about it was actually just copying and pasting the above snippet into my C# class and replacing each line with the appropriate C# calls to generate it. Here's what it looks like:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="c-sharp" name="code"&gt;    public static string Silverlight(this HtmlHelper html, string relativeControlPath, &lt;br /&gt;                                     Size size, object parameters)&lt;br /&gt;    {&lt;br /&gt;        var controlPath = VirtualPathUtility.ToAbsolute(relativeControlPath);&lt;br /&gt;&lt;br /&gt;        var objectTag = new TagBuilder(&amp;quot;object&amp;quot;)&lt;br /&gt;        {&lt;br /&gt;            Attributes = {&lt;br /&gt;                            {&amp;quot;data&amp;quot;, &amp;quot;data:application/x-silverlight-2,&amp;quot;},&lt;br /&gt;                            {&amp;quot;type&amp;quot;, &amp;quot;application/x-silverlight-2&amp;quot;},&lt;br /&gt;                            {&amp;quot;width&amp;quot;, size.Width.ToString()},&lt;br /&gt;                            {&amp;quot;height&amp;quot;, size.Height.ToString()},&lt;br /&gt;                        }&lt;br /&gt;        };&lt;br /&gt;&lt;br /&gt;        var innerHtml = new StringBuilder();&lt;br /&gt;        innerHtml.AppendFormat(ParamHtmlFormatString, &amp;quot;minRuntimeVersion&amp;quot;, &amp;quot;2.0.31005.0&amp;quot;);&lt;br /&gt;        innerHtml.AppendFormat(ParamHtmlFormatString, &amp;quot;autoUpgrade&amp;quot;, &amp;quot;true&amp;quot;);&lt;br /&gt;        innerHtml.AppendFormat(ParamHtmlFormatString, &amp;quot;source&amp;quot;, controlPath);&lt;br /&gt;&lt;br /&gt;        foreach (var param in new RouteValueDictionary(parameters))&lt;br /&gt;            innerHtml.AppendFormat(ParamHtmlFormatString, param.Key, param.Value);&lt;br /&gt;&lt;br /&gt;        innerHtml.AppendLine(&amp;quot;\n&amp;lt;!-- [ Silverlight not installed message here ] --/&amp;gt;&amp;quot;);&lt;br /&gt;&lt;br /&gt;        objectTag.InnerHtml = innerHtml.ToString();&lt;br /&gt;&lt;br /&gt;        return objectTag.ToString();&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;There are a couple interesting things going on in this snippet. First off, I start by resolving the absolute path to the Silverlight XAP; this needs to be resolved because this URL will be sent down to the client, and an application-relative path (starting with &amp;quot;~/&amp;quot;) does us no good in a browser. Next, I use the new &lt;a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.tagbuilder_members.aspx" target="_blank"&gt;System.Web.Mvc.TagBuilder&lt;/a&gt;&lt;strong&gt;&lt;/strong&gt; class which (as Reflector shows us) is what the framework uses to construct HTML in its Extension Methods (such as Html.ActionLink, Html.Form, etc.). I also supply it with a few standard attributes. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="height: 50px"&gt;&lt;img style="border-right-width: 0px; margin: -8px 20px 0px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" align="left" src="http://jchad.mediafreak.org/d/images/rabid_hamster.jpg" width="45" height="60" /&gt; &lt;br /&gt;&lt;br /&gt;  &lt;p style="margin-top: 10px"&gt;Note that I've hard-coded the Silverlight 2 version info and MIME type... I'm not recommending that you actually do this - it will most certainly attract rabid hamsters to come and eat your code - but for simplicity's sake in doing it in this example anyway.&lt;/p&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;By this point, you've probably got a pretty good idea about what's going on, but I want to point out one last thing - the usage of &lt;a href="http://msdn.microsoft.com/en-us/library/system.web.routing.routevaluedictionary.aspx" target="_blank"&gt;System.Web.Routing.RouteValueDictionary&lt;/a&gt;&lt;strong&gt;&lt;/strong&gt;. Again taking a cue from the MVC framework itself, I’m using this incredibly helpful (albeit poorly named) class from the new System.Web.Routing namespace to convert anonymous types into a set of key-value pairs that we can then use in our Silverlight method to dynamically add parameters (which are, conveniently enough, simply name/value pairs!). &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;After it's all done setting everything up, the Silverlight method asks the TagBuilder to render out the markup for our new object tag and its children, and with that, we're pretty much done! &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-916857720309146962?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=wbuYx8kJ1DU:Bf-O7ubsq7g:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=wbuYx8kJ1DU:Bf-O7ubsq7g:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=wbuYx8kJ1DU:Bf-O7ubsq7g:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=wbuYx8kJ1DU:Bf-O7ubsq7g:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/wbuYx8kJ1DU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/wbuYx8kJ1DU/helping-silverlight-and-aspnet-mvc-work.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/05/helping-silverlight-and-aspnet-mvc-work.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-7666720060394767064</guid><pubDate>Fri, 12 Jun 2009 06:52:00 +0000</pubDate><atom:updated>2009-06-12T02:52:39.599-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><title>Using WebForms Controls in ASP.NET MVC: The Unholy (and Cost-Effective!) Union</title><description>&lt;p&gt;My buddy and fellow Infragisticsian, Craig Shoemaker, &lt;a href="http://community.infragistics.com/blogs/craig_shoemaker/archive/2009/06/11/netadvantage-asp-net-in-asp-net-mvc.aspx" target="_blank"&gt;posted a blog post&lt;/a&gt; and &lt;a href="http://community.infragistics.com/aspnet/media/p/98918.aspx" target="_blank"&gt;a video on our Community site&lt;/a&gt; showing how you can use the current Infragistics Web controls in ASP.NET MVC.&amp;#160; Craig’s posts are invaluable because he shows you how you can &lt;strong&gt;leverage your current investment&lt;/strong&gt; in the WebForms controls &lt;em&gt;you’ve already purchased&lt;/em&gt; by using them in your ASP.NET MVC applications.&lt;/p&gt;  &lt;p&gt;I worked with Craig on some parts of the sample he’s discussing (which is to say that I wrote about a dozen lines of code and then sat back while he did the rest…) and I can say that we’re not trying to play any tricks here – we’re not trying to &lt;a href="http://en.wikipedia.org/wiki/Snake_oil" target="_blank"&gt;sell you snake oil&lt;/a&gt;.&amp;#160; In fact, in his post, he admits almost immediately that mixing WebForms server controls and MVC is an “unholy union” – something I (and I’m sure most other MVC-ers) whole-heartedly agree with. &lt;/p&gt;  &lt;p&gt;We all know that WebForms controls are not &amp;quot;MVC controls&amp;quot; (a concept which has yet to be clearly defined) and vice-versa.&amp;#160; However, that’s not to say that the product offerings available &lt;em&gt;today&lt;/em&gt; can’t offer you a good of value if applied deliberately and judiciously.&amp;#160; That subjective phrase, “deliberately and judiciously”, is exactly what Craig does a great job of addressing with these posts by offering guidance on when, where, and how you might use these existing controls.&amp;#160; Hopefully, this guidance can help get you through until there are true “MVC controls” available for you to use.&amp;#160; After all, you may need to make some compromises and sacrifices along the way, but it still beats writing this stuff from scratch!&lt;/p&gt;  &lt;p&gt;But hey - don’t let me jam my opinions down your throat.&amp;#160; What do &lt;em&gt;you&lt;/em&gt; think?&amp;#160; Is this “unholy union” so unholy that it’s actually blasphemous?&amp;#160; Do you like this approach?&amp;#160; Are there any ways it could be better?&amp;#160; The only way the situation can improve is if we developers all constructively contribute to the larger discussion about what &lt;em&gt;we&lt;/em&gt; want to see happen in this space… so let’s get it started!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-7666720060394767064?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=DUY8R5xRhk4:7UzYMUcN-gw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=DUY8R5xRhk4:7UzYMUcN-gw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=DUY8R5xRhk4:7UzYMUcN-gw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=DUY8R5xRhk4:7UzYMUcN-gw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/DUY8R5xRhk4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/DUY8R5xRhk4/using-webforms-controls-in-aspnet-mvc.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/06/using-webforms-controls-in-aspnet-mvc.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-5134514116012825791</guid><pubDate>Sun, 05 Jul 2009 07:12:00 +0000</pubDate><atom:updated>2009-07-05T03:23:50.024-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">NHibernate</category><category domain="http://www.blogger.com/atom/ns#">review</category><title>Book Review:  NHibernate in Action</title><description>&lt;p&gt;Over a year ago I wrote about my &lt;a href="http://jesschadwick.blogspot.com/2008/04/nhibernate-lazy-loading-snafu.html" target="_blank"&gt;NHibernate Lazy Loading Snafu&lt;/a&gt; and in that blog post it was pretty clear I was mostly clueless when it came to NHibernate.&amp;#160; Unfortunately, that hasn’t changed much in the past year, so I was incredibly eager to get my hands on the new Manning book, &lt;a href="http://www.amazon.com/gp/product/1932394923?ie=UTF8&amp;amp;tag=creativerea0a-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1932394923" target="_blank"&gt;NHibernate in Action&lt;/a&gt;.&amp;#160; Believe me, it did not disappoint.&lt;/p&gt;  &lt;p&gt;I’d argue that this book may be more appropriately naming something along the lines of “ORM in Action (with a focus on NHibernate)” because it is not only a bible for understanding and using NHibernate, but for ORM concepts in general!&amp;#160; The authors skillfully intertwine detailed and insightful discussion of general database, ORM, and enterprise development concepts with the nitty-gritty implementation details of NHibernate, all in an easy-to-read manner.&amp;#160; Beginning with a tour of many of the various ORM (and ORM-ish) solutions available to .NET developers and ending with a few chapters dedicated to discussing best practices of enterprise application development, this is a very well-rounded book that is easily digested by developers of pretty much any skill level.&amp;#160; I knew only high-level details about NHibernate and had a few mis-guided attempts at implementing it by myself prior to reading this book, but now I feel incredibly confident that I will be able to create plenty of NHibernate-driven applications with ease.&amp;#160; Another great benefit is the comfort I get from knowing that when I hit any more snafus in the future, it is obviously that this book will be there as a solid reference to help get me through.&lt;/p&gt;  &lt;p&gt;The cons?&amp;#160; It'd be nice if the book discussed NHibernate 2 &amp;amp; .NET 3.x functionality (like LINQ-to-NHibernate), but I think those expectations are somewhat unrealistic. Because of its open source nature, NHibernate is a living organism with stark contrast to a published book. Due to this contrast, I am more interested in a text that can explain the fundamental concepts than an incredibly in-depth (and quickly obsolete!) explanation of the technical implementation of those concepts.&lt;/p&gt;  &lt;p&gt;When it comes down to it, this is a great book that delivers on its promises and provides a comprehensive look at NHibernate in Action and how you can get it working for you.&amp;#160; I’m just gonna come right out and say it – this is the &lt;strong&gt;NHibernate Bible&lt;/strong&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-5134514116012825791?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=uLGY15N2hcQ:tUoehU_8UY0:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=uLGY15N2hcQ:tUoehU_8UY0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=uLGY15N2hcQ:tUoehU_8UY0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=uLGY15N2hcQ:tUoehU_8UY0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/uLGY15N2hcQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/uLGY15N2hcQ/book-review-nhibernate-in-action.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/07/book-review-nhibernate-in-action.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-5477073899087090192</guid><pubDate>Sun, 26 Jul 2009 03:36:00 +0000</pubDate><atom:updated>2009-07-25T23:36:05.336-04:00</atom:updated><title>On to the Next Set of Challenges!</title><description>&lt;p&gt;You won’t see many of my blog entries get too personal, largely due to the fact that I am a relatively private person to start with, but also because this is supposed to be a technical blog.&amp;#160; That said, I wanted to break stride for one post and speak to the fact that I am leaving Infragistics and have now moved on to new challenges by deciding to start consulting.&lt;/p&gt;  &lt;p&gt;It has been a great 3.5 years at Infragistics for me and I can not speak highly enough of my time there.&amp;#160; Never before have I worked in an environment so rich with knowledge, intensity, and passion.&amp;#160; It is an environment of productivity; it’s where teams of great minds and talented professionals join forces to produce amazing results, wasting no time in shipping amazing stuff.&amp;#160; It’s also fast-paced: a few of us had at one point discussed the concept of “Infragistics Time” joking that one or two days at IG would be the equivalent of up to a week anywhere else… and I’m not just talking deliverables.&amp;#160; To put it another way, I joined Infragistics as a Senior Web Developer, but after only one year in, I’d learned more and gained more experience than the entire rest of my career combined.&lt;/p&gt;  &lt;p&gt;I didn’t do all this learning in a silo.&amp;#160; Reporting to the guy I would end up calling my mentor - &lt;a href="http://www.ambroselittle.com/" target="_blank"&gt;Ambrose Little&lt;/a&gt; - was a crucial aspect of my development.&amp;#160; Until you’ve actually met him (and if you haven’t had the pleasure, the least you can do is &lt;a href="http://twitter.com/ambroselittle" target="_blank"&gt;follow him on Twitter&lt;/a&gt;!), it’s hard to describe just how awesome this guy is.&amp;#160; Crazy smart, level-headed, patient, and open-minded are just a few words that come to mind.&amp;#160; He guided both me and our group to continuously increasing levels of success… and he was really only doing it “part-time”, having another whole set of responsibilities above and beyond managing me and the website(s)!&amp;#160; It was also through this team that Ambrose led that I was able to foster deep personal and professional relationships with &lt;a href="http://twitter.com/ToddSnyder" target="_blank"&gt;Todd Snyder&lt;/a&gt; and &lt;a href="http://twitter.com/edblankenship" target="_blank"&gt;Ed Blankenship&lt;/a&gt; – two guys that I guarantee will continue to be two of my most valuable friends and colleagues for the rest of my professional (and personal!) life.&lt;/p&gt;  &lt;p&gt;As an active supporter of the .NET community, Infragistics also introduced me to the amazing rewards of community involvement.&amp;#160; In a matter of months I had gone from never having attended a local user group meeting to becoming a presenter and eventually assuming leadership of or local group, &lt;a href="http://www.njdotnet.net" target="_blank"&gt;NJDOTNET&lt;/a&gt; and later earning Microsoft’s MVP award!&amp;#160; This was all great fun, but I only recently realized just how deeply this involvement had affected me when my recent job search had me writing out my professional priorities and “community involvement” emerged as #1!&amp;#160; And, I owe all of this to Infragistics’ support, as well as trying to follow in the footsteps of both Ambrose and Jason Beres… which is not an easy thing to do!&lt;/p&gt;  &lt;p&gt;I didn’t mean for this post to be a biography of my tenure at Infragistics, and as such I am focusing on those with whom I worked the longest and who had the deepest impact on my life. Unfortunately, that means leaving out the myriad other great folks that I was lucky enough to meet and work with.&amp;#160; So, I’m sorry that I am leaving so many of you out, but you know who you are and – even if I wasn’t able to mention you specifically – thank you for making my time at Infragistics a great one!&amp;#160; Farewell everyone – I’m sure I’ll see you all again sooner or later!&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;font style="background-color: #ffffff"&gt;&lt;strong&gt;Shameless Plug:&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;    &lt;p&gt;&lt;font style="background-color: #ffffff"&gt;So…&amp;#160; as you may have noticed, I opened up this post by mentioning that I decided to start consulting.&amp;#160; That means that if you’re looking for some help to knock out that next awesome project of yours, &lt;strong&gt;please feel free to contact me!&lt;/strong&gt;&amp;#160; &lt;/font&gt;&lt;/p&gt;    &lt;p&gt;&lt;font style="background-color: #ffffff"&gt;If you’re interested, here’s a link to &lt;a href="http://www.jesschadwick.com/Resume.docx" target="_blank"&gt;my resume in Word 2007 format&lt;/a&gt;.&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-5477073899087090192?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=w3Ty460FwrI:CeUdK07crhc:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=w3Ty460FwrI:CeUdK07crhc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=w3Ty460FwrI:CeUdK07crhc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=w3Ty460FwrI:CeUdK07crhc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/w3Ty460FwrI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/w3Ty460FwrI/on-to-next-set-of-challenges.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/07/on-to-next-set-of-challenges.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-2542779881126140468</guid><pubDate>Fri, 04 Sep 2009 06:03:00 +0000</pubDate><atom:updated>2009-09-04T02:03:17.353-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">integration</category><category domain="http://www.blogger.com/atom/ns#">source control</category><title>Issue Tracking Integration with Subversion &amp; TortoiseSVN</title><description>&lt;p&gt;Many development shops have the requirement to associate any code changes to a backlog item or defect to help track the time and energy spent working against a particular featureset.&amp;#160; This need is so prevelant, that the awesome TortoiseSVN Windows Explorer Subversion extension actually has some special settings you can use to help make your life a little easier.&lt;/p&gt;  &lt;p&gt;Generally when demoing something like this, I like to show the finished result and then jump back to the beginning and follow the whole process step-by-step, but I’m going to break stride with this post.&amp;#160; I’m just gonna do it.&amp;#160; Here it goes:&lt;/p&gt;  &lt;h3&gt;How do you associate a backlog item/bug/issue ID with a check-in?&lt;/h3&gt;  &lt;p&gt;&lt;strong&gt;Add the “bugtraq:message” property to the root folder of your repository, and set it to something like “Backlog ID: %BUGID%”.&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Yep, that’s it.&amp;#160; I know - crazy, right?&amp;#160; Next time you go to check in, you’ll see something like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_sH3XWh6OzVE/SqCtj73BcuI/AAAAAAAAAG0/cE8OAnztCDk/s1600-h/image%5B16%5D.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" src="http://lh3.ggpht.com/_sH3XWh6OzVE/SqCtkJ7-r7I/AAAAAAAAAG4/O25bS2j4SDE/image_thumb%5B8%5D.png?imgmax=800" width="374" height="419" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;For those of you who have never used &lt;a href="http://svnbook.red-bean.com/en/1.0/ch07s02.html" target="_blank"&gt;Subversion properties&lt;/a&gt;, the easiest way to add one is to right-click on your Subversion folder, then select “TortoiseSVN &amp;gt; Properties” which will bring you to the dialog where you can add and edit your Subversion properties.&amp;#160; All of the features I discuss in this post are unlocked using these properties.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Then, after filling in a value for the Backlog ID and hitting OK to complete the check-in, you can visit the history logs and see that TortoiseSVN has oh-so-nicely inserted the Backlog ID into your checkin comment for you, like so:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_sH3XWh6OzVE/SqCtmKBhVVI/AAAAAAAAAG8/ErDvObW8PG4/s1600-h/image%5B4%5D.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" src="http://lh4.ggpht.com/_sH3XWh6OzVE/SqCtmmfdtTI/AAAAAAAAAHA/7EutGK5qZTE/image_thumb%5B2%5D.png?imgmax=800" width="398" height="300" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Some of you might be thinking, “But hey – I could have just typed that myself!”&amp;#160; Yeah, sure – if you wanna waste a bunch of time typing the same thing in every comment, this probably won’t be of much help to you… but wait – there’s more!&lt;/p&gt;  &lt;h3&gt;Make that integration experience a little bit nicer&lt;/h3&gt;  &lt;p&gt;For those of you who were underwhelmed with the first section, let’s dive a little deeper and check out some of the other bug tracking properties that TortoiseSVN has made available to us that allow us to customize this behavior:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;bugtraq&lt;/b&gt;:&lt;b&gt;url&lt;/b&gt; – now &lt;em&gt;this&lt;/em&gt; is the stuff you’ve been waiting for!&amp;#160; If you set this property with a URL pattern containing the %BUGID% placeholder, TortoiseSVN will be nice enough to turn those nifty little messages into &lt;strong&gt;direct links to your issue tracking system&lt;/strong&gt;!&amp;#160; Naturally, this link will be different for every issue tracking system, but assuming your system allows you link directly to an issue via it’s ID, this is a pretty sweet option.&amp;#160; Here’s an example:&amp;#160; &lt;br /&gt;By setting the &lt;strong&gt;bugtraq:url &lt;/strong&gt;property to&lt;strong&gt; “&lt;/strong&gt;&lt;em&gt;&lt;a href="http://myversioncontrol/issues/%BUGID%"&gt;http://myversioncontrol/issues/%BUGID%&lt;/a&gt;”&lt;/em&gt;, my previous history message (shown below) now contains a link directly into my issue tracking system! &lt;/li&gt; &lt;/ul&gt; &lt;a href="http://lh4.ggpht.com/_sH3XWh6OzVE/SqCtm-70jBI/AAAAAAAAAHE/C9ue42moaTo/s1600-h/image%5B19%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="image" border="0" alt="image" align="left" src="http://lh5.ggpht.com/_sH3XWh6OzVE/SqCtnAOoA7I/AAAAAAAAAHI/pnKtV1O4r6I/image_thumb%5B11%5D.png?imgmax=800" width="306" height="160" /&gt;&lt;/a&gt; &lt;a href="http://lh6.ggpht.com/_sH3XWh6OzVE/SqCtnkR7zdI/AAAAAAAAAHM/xRpod1I8yEA/s1600-h/image%5B20%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="image" border="0" alt="image" align="left" src="http://lh3.ggpht.com/_sH3XWh6OzVE/SqCtn2UHv4I/AAAAAAAAAHQ/6fhMXspmwj4/image_thumb%5B12%5D.png?imgmax=800" width="316" height="238" /&gt;&lt;/a&gt;   &lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;  &lt;ul style="clear: both"&gt;   &lt;li&gt;&lt;b&gt;bugtraq:warnifnoissue&lt;/b&gt; – this awesome setting tells Tortoise to yell at the dev (shown to the right) if they haven’t provided an Issue #.&amp;#160; It’s purely a client-side setting and provides no server-side validation so it’s not going to &lt;em&gt;force&lt;/em&gt; the users to associate an issue #, but it sure is a nice reminder.&amp;#160; &lt;br /&gt;Note:&amp;#160; If you really want to perform server-side validation, stay tuned – a post on that topic should be coming up soon! &lt;a href="http://lh4.ggpht.com/_sH3XWh6OzVE/SqCtoUva5dI/AAAAAAAAAHU/HmmJdhUoYuE/s1600-h/clip_image001%5B7%5D%5B6%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="clip_image001[7]" border="0" alt="clip_image001[7]" align="left" src="http://lh5.ggpht.com/_sH3XWh6OzVE/SqCtovIqBmI/AAAAAAAAAHY/O-kFMfVlbUU/clip_image001%5B7%5D_thumb%5B4%5D.jpg?imgmax=800" width="351" height="110" /&gt;&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;ul style="clear: both"&gt;   &lt;li&gt;&lt;b&gt;bugtraq:append&lt;/b&gt; – if you’re like me and want the backlog/issue ID right at the beginning of the message instead of the end, you can set the “bugtraq:append” property to “false” so it &lt;b&gt;pre&lt;/b&gt;pends the ID snippet to the beginning of the log message instead of appending it to the end (as is the default behavior shown earlier).       &lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_sH3XWh6OzVE/SqCto17QBOI/AAAAAAAAAHc/yya-uTY297Y/s1600-h/clip_image001%5B5%5D%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image001[5]" border="0" alt="clip_image001[5]" src="http://lh4.ggpht.com/_sH3XWh6OzVE/SqCtpEijObI/AAAAAAAAAHg/Dyje2Uw_tWE/clip_image001%5B5%5D_thumb%5B2%5D.jpg?imgmax=800" width="341" height="227" /&gt;&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;ul style="clear: both"&gt;   &lt;li&gt;&lt;strong&gt;bugtraq:label&lt;/strong&gt; – if you go waaay back to the first screenshot in this post, you’ll see that the label for the Backlog ID input box had the awkward default value of “Bug-ID / Issue-Nr:”&amp;#160; This bugged the heck out of me, and I wanted to change it to match my message of “Backlog ID:”, and luckily the &lt;strong&gt;bugtraq:label&lt;/strong&gt; property let’s you do just that!&amp;#160;&amp;#160; I just set the value of &lt;strong&gt;bugtraq:label&lt;/strong&gt;=Backlog ID:, and I was good to go! &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Well, I hope you found these useful and that they help you in your quest to write better software.&amp;#160; As always, if you know of a better way to do this or have any comments, suggestions, or questions, please feel free to comment below.&lt;/p&gt;  &lt;p&gt;Good luck, and happy coding!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-2542779881126140468?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=UfvWG8GCOiE:14LckRqPPnM:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=UfvWG8GCOiE:14LckRqPPnM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=UfvWG8GCOiE:14LckRqPPnM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=UfvWG8GCOiE:14LckRqPPnM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/UfvWG8GCOiE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/UfvWG8GCOiE/issue-tracking-integration-with.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/09/issue-tracking-integration-with.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3016850660716023308.post-6058845386770402132</guid><pubDate>Fri, 16 Oct 2009 06:43:00 +0000</pubDate><atom:updated>2009-10-16T02:48:29.487-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">integration</category><category domain="http://www.blogger.com/atom/ns#">cool tools</category><category domain="http://www.blogger.com/atom/ns#">TFS</category><category domain="http://www.blogger.com/atom/ns#">MSTest</category><category domain="http://www.blogger.com/atom/ns#">VSTS</category><title>TFS Ain’t So Expensive Anymore</title><description>&lt;p&gt;Those of you who scoffed at the enormous price tag on previous releases of Team Foundation Server will be happy to hear about &lt;a href="http://blogs.msdn.com/bharry/archive/2009/10/01/tfs-2010-for-sourcesafe-users.aspx" target="_blank"&gt;“TFS Basic” - the new offering of TFS 2010&lt;/a&gt; (or as Brian calls it, “TFS for SourceSafe users”&amp;#160; **shudder**).&amp;#160; Presumably, this new offering includes all of the functionality that small development shops will need to thrive on TFS, while still offering a sane upgrade path.&lt;/p&gt;  &lt;p&gt;I haven’t been able to find exactly how much this new SKU is going to run you, but from what I’ve seen it will not be $0.00 (AKA: free).&amp;#160; As &lt;a href="http://jesschadwick.blogspot.com/2009/09/issue-tracking-integration-with.html" target="_blank"&gt;my very last post&lt;/a&gt; may tell you, I am a huge fan of the free &amp;amp; open source offerings out there, but – as my last post shows – making these disparate projects integration together can quite often mean a whole lot of time and energy.&amp;#160; Even with the astronomical price tag of previous versions, the out-of-the-box integration of Source Control, Continuous Integration, and Change Tracking has always been incredibly alluring to me.&amp;#160; And, for those who really desired it, it was probably worth the cost.&amp;#160; The exciting part of this announcement is that you can now get this powerful integration – sans advanced features – for a fraction of what the full TFS system used to cost…&amp;#160; and that is pretty damn cool if you ask me.&lt;/p&gt;  &lt;p&gt;Will I make the switch from Subversion+CruiseControl+[whatever change tracking and planning tool I’m using]?&amp;#160; Will I solicit my employer to switch?&amp;#160; No.&amp;#160; It’s nice to know that if one of the components isn’t working out for us or we find something better, we can replace just that one component and leave the others in place.&amp;#160; Additionally - while the initial pain in getting these open source solutions wired together can be substantial - once the initial price of time and effort is paid, it rarely gets in the way again.&amp;#160; However, those are existing installations I’m referring to;&amp;#160; for new projects, I will most definitely be evaluating TFS Basic along with the others and I expect that the savings we’d realize in integration alone will be enough to make it a leading contender.&lt;/p&gt;  &lt;p&gt;For those of you who have never had the pleasure of using Team Foundation Server, I strongly suggest you go grab these bits and try it out.&amp;#160; Sure, it’s got its downsides (as anyone who &lt;a href="http://twitter.com/jchadwick" target="_blank"&gt;follows me on Twitter&lt;/a&gt; knows), but it is also one hell of a nice product and certainly worth checking out.&amp;#160; What’s more – with TFS Basic, you no longer need to be in a server environment – you can feel free to install it on your local development environment!&amp;#160; Go download and install the bits and come back here and let me know what you think!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3016850660716023308-6058845386770402132?l=jesschadwick.blogspot.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=rGHHliTYX9c:Iixk420HWDQ:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=rGHHliTYX9c:Iixk420HWDQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?i=rGHHliTYX9c:Iixk420HWDQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JessChadwick?a=rGHHliTYX9c:Iixk420HWDQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JessChadwick?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JessChadwick/~4/rGHHliTYX9c" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JessChadwick/~3/rGHHliTYX9c/tfs-aint-so-expensive-anymore.html</link><author>jesschadwick@gmail.com (Jess Chadwick)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://jesschadwick.blogspot.com/2009/10/tfs-aint-so-expensive-anymore.html</feedburner:origLink></item></channel></rss>
