<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1240921806131117404</id><updated>2012-02-16T14:24:50.647-08:00</updated><category term='Webforms'/><category term='Reading list'/><category term='TDD'/><category term='MVC'/><category term='Agile'/><category term='Geo Tracks'/><category term='Source Control'/><category term='Sql Server'/><category term='TekPub Starter Site'/><category term='Stored Procedures'/><category term='Fluent NHibernate'/><category term='Supervising Controller'/><category term='Training'/><category term='NCharlie'/><title type='text'>Charlie Solomon</title><subtitle type='html'>Saving the world, one line of code at a time.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>27</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-5654772888551910393</id><published>2011-01-28T20:47:00.000-08:00</published><updated>2011-01-28T21:09:14.156-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Reading list'/><title type='text'>January sum-up</title><content type='html'>No code in this post... My wife bought me some books and I've been enjoying them late into our January nights, thanks in part to a chest cold that doesn't like when I lay down.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.amazon.com/Same-Kind-Different-As-Me/dp/tags-on-product/0849900417"&gt;Same Kind of Different as Me&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Inspiring story of an ex-sharecropper turned homeless, and how his path crosses someone who listens to God and acts on what she hears.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.amazon.com/Gods-Smuggler-John-Sherrill/dp/0800793013"&gt;God's Smuggler&lt;/a&gt;&lt;/div&gt;&lt;div&gt;The story of Brother Andrew's ministry bringing contraband bibles behind the Iron Curtain.  Andrew also listens carefully, and acts... seeing a theme here.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.amazon.com/Son-Hamas-Gripping-Political-Unthinkable/dp/1414333072"&gt;Son of Hamas&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Very revealing picture of life in the middle of the Israeli-Palestinian conflict.  Engaging, interesting... filled with perspectives that were new to me.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.amazon.com/Born-Standing-Up-Comics-Life/dp/1416553657"&gt;Born Standing Up&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Steve Martin's auto-biography of his stand-up comedy years.  Interesting, funny, revealing story about making it as a comic.  (Ok, I bought this one for my wife then borrowed it.)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-5654772888551910393?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/5654772888551910393/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2011/01/january-sum-up.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/5654772888551910393'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/5654772888551910393'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2011/01/january-sum-up.html' title='January sum-up'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-2130484701627631857</id><published>2010-09-29T20:44:00.000-07:00</published><updated>2010-09-29T20:53:49.955-07:00</updated><title type='text'>My team is hiring</title><content type='html'>Looking for a job?  Are you a mid-level to expert web developer in C#, ASP.Net and Sql Server?  My team builds web apps for the Navy, and we need some help.&lt;div&gt;&lt;br /&gt;&lt;div&gt;We have two requisitions open for web developers.  The jobs are in Ventura County, CA or Kern County, CA.  Please apply using the links below:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Mid-level experience:&lt;/div&gt;&lt;div&gt;&lt;a href="https://www.cytiva.com/jacobs/ext/detail.asp?jacobs13535"&gt;https://www.cytiva.com/jacobs/ext/detail.asp?jacobs13535&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;More experience:&lt;/div&gt;&lt;div&gt;&lt;a href="https://www.cytiva.com/jacobs/ext/detail.asp?jacobs13536"&gt;https://www.cytiva.com/jacobs/ext/detail.asp?jacobs13536&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-2130484701627631857?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/2130484701627631857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/09/my-team-is-hiring.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2130484701627631857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2130484701627631857'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/09/my-team-is-hiring.html' title='My team is hiring'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-4247604327261296919</id><published>2010-09-03T13:31:00.001-07:00</published><updated>2010-09-03T13:31:14.828-07:00</updated><title type='text'>Activating an upgrade edition of Windows 7 after a clean install</title><content type='html'>&lt;p&gt;In my experience, technical support calls are only frustrating because of vocabulary problems.&amp;#160; If the customer could convey the problem using vocabulary the support rep is familiar with, and if the rep can communicate the solution using words the customer understands, bingo!&lt;/p&gt;  &lt;p&gt;I spent a good hour on the phone today trying to activate Windows 7 after I installed a new hard drive in my MacBook Pro.&amp;#160; I was very pleased with the outcome (I’m active!), and with the technical support rep who finally helped me.&amp;#160; But, to get to him, I had to convince two reps before him that what I was doing warranted further help.&amp;#160; The first rep in the Activation Center said I had an “invalid product key”, and that I would have to talk with customer support.&amp;#160; The second rep told me they no longer support activating Windows on Mac computers with a clean install, and I would have to reinstall Windows XP, then perform an upgrade install (really?)&amp;#160; I did not accept that answer (since it would take me the rest of the day to reinstall 2 versions of Windows AND re-setup my development machine and projects).&amp;#160; After staying on hold for a supervisor for a few minutes he created a case number for me with the Windows technical support team.&lt;/p&gt;  &lt;p&gt;The key vocabulary words (for those of you who may need to call and talk to someone about this yourself) are: “Custom Install” vs. “Upgrade Install”, and using the MSDT tool to change my activation file from “custom” to “upgrade” so that my product key would work.&lt;/p&gt;  &lt;p&gt;Here is the email explaining my experience with the Windows team:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;Hi Charlie,       &lt;br /&gt;This is Adrian with Microsoft Windows Technical Support.&amp;#160; &lt;br /&gt;It was my pleasure to work with you on your Windows service request [case # deleted].&amp;#160; I hope that you were happy with the service provided to you.        &lt;br /&gt;Based on our last conversation it appears that this service request is resolved and ready to be archived.&amp;#160; If this is not correct or if you are not happy with the support we've provided please let us know as soon as possible.&amp;#160; My goal is to ensure that your experience with Microsoft Windows Technical Support leaves you pleased with our products and services.        &lt;br /&gt;Here is a summary of the key points of the service request for your records:         &lt;br /&gt;ACTION:        &lt;br /&gt;Charlie, you were trying to activate the copy of Windows 7 Ultimate.        &lt;br /&gt;RESULT:        &lt;br /&gt;You were not able to activate Windows 7 Ultimate as got an error '0xC004f061'.        &lt;br /&gt;CAUSE:        &lt;br /&gt;You were using an upgrade DVD to do a custom installation and the activation files installed were for custom and not for upgrade.        &lt;br /&gt;RESOLUTION:        &lt;br /&gt;Charlie, we used the MSDT (Microsoft Support Diagnostic Tool), changed the activation file from custom to upgrade and activated Windows 7 Ultimate.        &lt;br /&gt;If you have any feedback regarding Microsoft support, we would be glad to hear from you.&amp;#160; If you would feel more comfortable speaking with someone else regarding my service, Rajiv, my manager, would be very happy to hear your comments and suggestions.&amp;#160; You may reach my manager by sending an email to [manager’s email address].        &lt;br /&gt;Thank you for contacting Microsoft Windows Technical Support.&lt;/em&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/1240921806131117404-4247604327261296919?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/4247604327261296919/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/09/activating-upgrade-edition-of-windows-7.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/4247604327261296919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/4247604327261296919'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/09/activating-upgrade-edition-of-windows-7.html' title='Activating an upgrade edition of Windows 7 after a clean install'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-796859754564486621</id><published>2010-06-13T00:03:00.001-07:00</published><updated>2010-06-13T00:10:40.482-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geo Tracks'/><title type='text'>Geo Tracks – 12 Jun 2010</title><content type='html'>&lt;p&gt;&lt;em&gt;What is Geo Tracks?&amp;#160; My &lt;a title="First Geo Tracks post" href="http://charliesolomon.blogspot.com/2009/04/geo-tracks-2009-apr-11.html"&gt;first post like this&lt;/a&gt; was a couple years ago, and it has been more than a year since my last Geo Tracks post… mainly because of a much shorter commute, also because I don’t have a Geo anymore!&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;After a year of local radio and NPR, I’m taking back my 40 minutes a day.&amp;#160; I purchased a &lt;a title="Philips cassette adapter on Amazon" href="http://www.amazon.com/Philips-USA-PH-62050-Cassette-Adapter/dp/B0007R4LI8"&gt;cassette adapter&lt;/a&gt; so I’m back in business… I’ll log the good parts of my commuter listening here.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;a title="Hanselminutes 216" href="http://www.hanselminutes.com/default.aspx?showID=234"&gt;Hanselminutes 216&lt;/a&gt; - Geek Relationship Tips with Scott's Wife.&amp;#160; Some practical ways Scott and Mo make their marriage work.&amp;#160; Good tips here about finances, setting expectations, assuming the best… I enjoyed it!&lt;/p&gt;  &lt;p&gt;&lt;a title="Hanselminutes 210" href="http://www.hanselminutes.com/default.aspx?showID=228"&gt;Hanselminutes 210&lt;/a&gt; – John Lam and the Science of Fitness.&amp;#160; I am a couch potato right now… so hearing John Lam’s enthusiasm for using sensors to calculate his power output in watts while cycling was a bit over the top for me.&amp;#160; It is cool to hear about the types of sensors that are becoming available to every day Joes, and how it is becoming more acceptable, even expected, to share your data/progress in social apps like Twitter or Facebook.&amp;#160; Scott even jumped on the bandwagon recently by posting his &lt;a title="LoseIt post on Twitter" href="http://twitter.com/shanselman/status/16027508652"&gt;LoseIt updates to Twitter&lt;/a&gt;.&amp;#160; I responded by reading about the &lt;a href="http://hundredpushups.com/"&gt;HundredPushups&lt;/a&gt; and &lt;a href="http://c25k.com/"&gt;Couch to 5K&lt;/a&gt; programs while eating a bag of chips.&lt;/p&gt;  &lt;p&gt;&lt;a title="Cornerstone Simi in iTunes" href="http://itunes.apple.com/us/podcast/cornerstone-simi-video-podcast/id85203845"&gt;Cornerstone Simi Podcast 05/30/2010&lt;/a&gt; – The End.&amp;#160; Francis Chan’s last message to his church in Simi Valley.&amp;#160; God has blessed me through this podcast, and I’m sad that Francis won’t be using this platform to teach anymore.&amp;#160; I’m excited to see how God will use him next though… and the good news is I have &lt;em&gt;a lot&lt;/em&gt; of Francis messages to catch up on.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://itunes.apple.com/us/podcast/cornerstone-simi-video-podcast/id85203845"&gt;Cornerstone Simi Podcast 05/02/2010&lt;/a&gt; – Salvation.&amp;#160; Francis compares salvation to harmonies in music, those weird 3D posters, and other things that you either “just suddenly get” or not.&amp;#160; He who has ears, let him hear.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-796859754564486621?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/796859754564486621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/06/geo-tracks-12-jun-2010.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/796859754564486621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/796859754564486621'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/06/geo-tracks-12-jun-2010.html' title='Geo Tracks – 12 Jun 2010'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-6136623644863872843</id><published>2010-06-05T15:07:00.000-07:00</published><updated>2010-06-05T15:12:21.169-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sql Server'/><category scheme='http://www.blogger.com/atom/ns#' term='Stored Procedures'/><category scheme='http://www.blogger.com/atom/ns#' term='Source Control'/><title type='text'>Tracking SQL Server procedure and function changes</title><content type='html'>&lt;p&gt;This &lt;a title="Stack Overflow question about batching procedures to files" href="http://stackoverflow.com/questions/568734/how-save-sql-stored-prodecures-to-sql-files-via-batch"&gt;Stack Overflow question&lt;/a&gt; inspired me to create a couple batch files today:&lt;/p&gt;  &lt;p&gt;1. gen_all.cmd (creates one text file for each stored procedure and function in a database)&lt;/p&gt;  &lt;p&gt;&lt;textarea style="width: 604px; height: 37px" wrap="off"&gt;@echo off sqlcmd -E -S .\SQLEXPRESS -d DATABASE -h-1 -Q &amp;quot;SET NOCOUNT ON SELECT ROUTINE_NAME FROM INFORMATION_SCHEMA.Routines WHERE ROUTINE_NAME NOT LIKE 'dt_%%' AND (ROUTINE_TYPE = 'PROCEDURE' OR ROUTINE_TYPE = 'FUNCTION')&amp;quot; -o &amp;quot;sp_list.txt&amp;quot; for /f %%a in (sp_list.txt) do sqlcmd -E -S .\SQLEXPRESS -d DATABASE -h-1 -Q &amp;quot;SET NOCOUNT ON SELECT N'IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N''[dbo].['+ROUTINE_NAME+N']'')) DROP '+ROUTINE_TYPE+N' [dbo].['+ROUTINE_NAME+']' FROM INFORMATION_SCHEMA.Routines WHERE ROUTINE_NAME = '%%a' UNION ALL SELECT N'GO' UNION ALL select [text] from sysobjects join syscomments on sysobjects.id = syscomments.id where sysobjects.[name] = '%%a' UNION ALL SELECT N'GO'&amp;quot; -o &amp;quot;ProcsAndFuncs\%%a.sql&amp;quot; del sp_list.txt&lt;/textarea&gt; &lt;/p&gt;  &lt;p&gt;2. gen_since.cmd (same as gen_all.cmd, but takes a date/time parameter and only generates files for procs/funcs that were created or altered since the specified date/time)&lt;/p&gt;  &lt;p&gt;&lt;textarea style="width: 604px; height: 37px" wrap="off"&gt;@echo off sqlcmd -E -S .\SQLEXPRESS -d DATABASE -h-1 -Q &amp;quot;SET NOCOUNT ON SELECT ROUTINE_NAME FROM INFORMATION_SCHEMA.Routines WHERE ROUTINE_NAME NOT LIKE 'dt_%%' AND (ROUTINE_TYPE = 'PROCEDURE' OR ROUTINE_TYPE = 'FUNCTION') AND (CREATED &amp;gt;= convert(datetime,'%1') OR LAST_ALTERED &amp;gt;= convert(datetime,'%1'))&amp;quot; -o &amp;quot;sp_list.txt&amp;quot; for /f %%a in (sp_list.txt) do sqlcmd -E -S .\SQLEXPRESS -d DATABASE -h-1 -Q &amp;quot;SET NOCOUNT ON SELECT N'IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N''[dbo].['+ROUTINE_NAME+N']'')) DROP '+ROUTINE_TYPE+N' [dbo].['+ROUTINE_NAME+']' FROM INFORMATION_SCHEMA.Routines WHERE ROUTINE_NAME = '%%a' UNION ALL SELECT N'GO' UNION ALL select [text] from sysobjects join syscomments on sysobjects.id = syscomments.id where sysobjects.[name] = '%%a' UNION ALL SELECT N'GO'&amp;quot; -o &amp;quot;ProcsAndFuncs\%%a.sql&amp;quot; del sp_list.txt&lt;/textarea&gt;&lt;/p&gt;  &lt;p&gt;Usage (to generate any files altered on or after Jun 5th):&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;gen_since 06/05/2010&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;I know there are some slick database change management tools out there, both OSS (&lt;a title="Tarantino project on Google Code" href="http://code.google.com/p/tarantino/"&gt;Tarantino&lt;/a&gt;) and commercial (&lt;a title="SQL Compare on Redgate.com" href="http://www.red-gate.com/products/SQL_Compare/index.htm"&gt;Redgate SQL Compare&lt;/a&gt;)… but I’m going to try using this simple batch file for a while as a nice, mindless way to save my DB objects as files that can be tracked in source control.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-6136623644863872843?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/6136623644863872843/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/06/tracking-sql-server-procedure-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/6136623644863872843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/6136623644863872843'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/06/tracking-sql-server-procedure-and.html' title='Tracking SQL Server procedure and function changes'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-3798385252236098879</id><published>2010-05-29T00:57:00.001-07:00</published><updated>2010-05-29T00:57:43.703-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TekPub Starter Site'/><category scheme='http://www.blogger.com/atom/ns#' term='MVC'/><title type='text'>Test driving the TekPub ASP.Net MVC2 Starter Site</title><content type='html'>&lt;p&gt;I like starter sites… we use one I put together at work (I called it BlankSolution).&amp;nbsp; They make it really fast to get up and running on a new project, AND to carry over improvements and best practices from project to project.&amp;nbsp; They are also great for learning.&lt;/p&gt; &lt;p&gt;Rob Conery released an &lt;a title="TekPub MVC 2 Starter Site on CodePlex" href="http://mvcstarter.codeplex.com/"&gt;MVC 2 starter site&lt;/a&gt; recently which looks really interesting.&amp;nbsp; Go check it out to see all the goodies it includes… then come back here to read about my experience using it to build up a real project (the NCharlie Task Board of course!)&lt;/p&gt; &lt;p&gt;Here we go, step by step (I’m following Rob’s &lt;a title="MVC 2 Starter Site 0.5 Announcement, Getting Started" href="http://blog.wekeroad.com/2010/05/24/mvc-starter-2"&gt;blog post&lt;/a&gt; announcing the 0.5 release):&lt;/p&gt; &lt;p&gt;1. Download the bits from CodePlex, extract the zip and open the solution.&amp;nbsp; Screeeech… error opening in VS 2008.&amp;nbsp; Something about a bad pointer in the Web.csproj file to MSBuild “v10.0” WebApplicationTargets.&amp;nbsp; A quick edit to the csproj file (changed v10.0 to v9.0) fixed that (I won’t be forking the starter project for fixes if I find them… just want to see how quickly I can get up and running tonight.)&lt;/p&gt; &lt;p&gt;2. Set up the app to use SubSonic (the ORM tool Rob wrote).&amp;nbsp; Using Subsonic is a “for now” choice, to quickly build up the database from POCO domain classes.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;- Global.asax.cs needs one change, on line 135.&amp;nbsp; Change “SiteEFSession” to “SubSonicSimple”.&lt;/p&gt; &lt;p&gt;- SubSonicSimple.cs needs one change, on line 18, add the name of the connection string the app will use, “SiteData”.&lt;/p&gt; &lt;p&gt;- The Web.config file doesn’t need to change, as long as your local database instance name is “SQLEXPRESS”.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;3. Add a new Class Library project to the solution to hold the domain classes.&amp;nbsp; I called it “Core”.&amp;nbsp; Make sure to add the “Core” project to the “Web” project’s references.&lt;/p&gt; &lt;p&gt;&lt;em&gt;(Side note: If you read through the &lt;a href="http://blog.wekeroad.com/2010/05/24/mvc-starter-2#comment-52003704"&gt;comments&lt;/a&gt; on Rob’s getting started post, he ripped some guy a new one for suggesting he put his domain entities in a separate project, and add separate “Model” classes that map between the Core and the UI… so proceed at your own risk!)&lt;/em&gt;&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;- Add the domain classes:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/TADI8FCqwsI/AAAAAAAAAEk/ZzXjR5aDkHA/s1600-h/Domain-objects%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Domain-objects" border="0" alt="Domain-objects" src="http://lh3.ggpht.com/_juzpwZu8gCw/TADI8Yzh5TI/AAAAAAAAAEo/BBXszKo13X0/Domain-objects_thumb.png?imgmax=800" width="244" height="203"&gt;&lt;/a&gt; &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;4. Modify HomeController.cs:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;- Add ISession as a private, injected member.&lt;/p&gt; &lt;p&gt;- Grab all the Board records from the database, ordered by Board name, and paged&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HomeController &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;Controller &lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ISession &lt;/span&gt;_session;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public &lt;/span&gt;HomeController(&lt;span style="color: #2b91af"&gt;ISession &lt;/span&gt;session)&lt;br /&gt;    {&lt;br /&gt;        _session = session;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ActionResult &lt;/span&gt;Index(&lt;span style="color: blue"&gt;int&lt;/span&gt;? page) {&lt;br /&gt;        &lt;span style="color: blue"&gt;var &lt;/span&gt;boards = _session.All&amp;lt;&lt;span style="color: #2b91af"&gt;Board&lt;/span&gt;&amp;gt;().OrderBy(x =&amp;gt; x.Name);&lt;br /&gt;        &lt;span style="color: blue"&gt;var &lt;/span&gt;list = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Board&lt;/span&gt;&amp;gt;(boards);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;return &lt;/span&gt;View(list);&lt;br /&gt;    }&lt;/pre&gt;&lt;pre class="code"&gt;&amp;nbsp;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;5. Control-F5 to build and run the site… it builds, but:&lt;/p&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_juzpwZu8gCw/TADI8qrrP2I/AAAAAAAAAEs/_ybkkbB7Zyc/s1600-h/Error-screen%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Error-screen" border="0" alt="Error-screen" src="http://lh5.ggpht.com/_juzpwZu8gCw/TADI800NfWI/AAAAAAAAAEw/Ofi9sspKSWY/Error-screen_thumb%5B1%5D.png?imgmax=800" width="504" height="227"&gt;&lt;/a&gt; &lt;/p&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;6. I had some problems.&lt;/p&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;p&gt;- First, my SQL Server instance is version 2005… Rob’s Site.mdf file in the App_Data folder is v655 (2008 I think), and it would not open.&amp;nbsp; I created a new “SiteData” blank database on my local instance, modify the connection string in Web.config, and tried again.&amp;nbsp; Same beautiful error screen I got before.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;- Second (I looked in the Event Viewer for this exception), I found this in the exception detail:&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;em&gt;Exception information: &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Exception type: InvalidOperationException &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Exception message: Can't decide which property to consider the Key - you can create one called 'ID' or mark one with SubSonicPrimaryKey attribute&lt;/em&gt; &lt;br /&gt;&lt;p&gt;…so, I added an “ID” property to by Board class, and tried again.&amp;nbsp; Error screen.&lt;br /&gt;&lt;p&gt;- This time the “Index.aspx” view is expecting the model to be “Web.Model.User”, and I’m passing it a list of Board entities to choke on… that makes sense.&amp;nbsp; So I changed the “Inherits” directive at the top of Index.aspx to expect IList&amp;lt;Core.Domain.Board&amp;gt;, and removed the one “EditorFor” statement on line 35, and…&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_juzpwZu8gCw/TADI9e6wB2I/AAAAAAAAAE0/fm42NXg8hog/s1600-h/Starter-home%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Starter-home" border="0" alt="Starter-home" src="http://lh6.ggpht.com/_juzpwZu8gCw/TADI9lNnjvI/AAAAAAAAAE4/C6dFk4GBPFI/Starter-home_thumb%5B1%5D.png?imgmax=800" width="504" height="292"&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;It’s alive!&amp;nbsp; It took me almost 2 hours to follow Rob’s getting started post to this point… I didn’t get to the part about switching SubSonic out for EF or LinqToSql.&amp;nbsp; I’ll leave that, &lt;em&gt;and&lt;/em&gt; making this into an actual Task Board, for next time.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-3798385252236098879?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/3798385252236098879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/05/test-driving-tekpub-aspnet-mvc2-starter.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/3798385252236098879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/3798385252236098879'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/05/test-driving-tekpub-aspnet-mvc2-starter.html' title='Test driving the TekPub ASP.Net MVC2 Starter Site'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_juzpwZu8gCw/TADI8Yzh5TI/AAAAAAAAAEo/BBXszKo13X0/s72-c/Domain-objects_thumb.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-4180860247452827117</id><published>2010-04-10T21:04:00.001-07:00</published><updated>2010-04-10T21:04:30.920-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVC'/><category scheme='http://www.blogger.com/atom/ns#' term='NCharlie'/><title type='text'>NCharlie Task Board – MVC Version</title><content type='html'>&lt;p&gt;It’s unfortunate that paid work has to take precedence over fun blog projects like this… but that’s life!&amp;#160; I have an hour or so tonight, so I’m jumping back in to this task board project.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_juzpwZu8gCw/S8FKQ46vp1I/AAAAAAAAAEE/wFH1X-lCXoA/s1600-h/UI.Mvc-project-tree%5B5%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; margin-left: 0px; border-top: 0px; margin-right: 0px; border-right: 0px" title="UI.Mvc-project-tree" border="0" alt="UI.Mvc-project-tree" align="right" src="http://lh6.ggpht.com/_juzpwZu8gCw/S8FKRDEAq4I/AAAAAAAAAEI/3uvtwe7GqZ8/UI.Mvc-project-tree_thumb%5B3%5D.png?imgmax=800" width="281" height="431" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Last time (February, ‘10!) I &lt;a title="ncharlie-task-board-webforms-version" href="http://charliesolomon.blogspot.com/2010/02/ncharlie-task-board-webforms-version.html"&gt;wrote&lt;/a&gt; about the Webforms implementation of the task board.&amp;#160; That was a good exercise for me, because I’ll be able to take many of the patterns and tools from that little project directly into my “for pay” projects, which are still all Webforms.&lt;/p&gt;  &lt;p&gt;This MVC version will take up more than one post; I will try to keep the posts a bit shorter, and I want to record the implementation details with enough depth that I’ll be able to use these notes later to quickly ramp up my team on MVC projects when we get there.&lt;/p&gt;  &lt;p&gt;This (image at right) is the initial project tree from Solution Explorer.&amp;#160; You can see some of the same elements from the Webforms version (AutoMapper, StructureMap, a Master page, an NHibernate configuration file and TransactionBoundaryModule that implements the “Session per Request” pattern for data access operations.)&amp;#160; This UI.Mvc project references the same “Core” project that the Webforms project references.&lt;/p&gt;  &lt;p&gt;To quickly get started with the “Model-View-Controller” pattern in this project, I created the TaskBoardController class and added one ActionResult method named “Index”:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ActionResult &lt;/span&gt;Index()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;var &lt;/span&gt;board = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Board&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;My hard coded test board&amp;quot;&lt;/span&gt;, 1, 1);&lt;br /&gt;    &lt;span style="color: blue"&gt;var &lt;/span&gt;column = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Column&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;Col1&amp;quot;&lt;/span&gt;, board);&lt;br /&gt;    &lt;span style="color: blue"&gt;var &lt;/span&gt;task = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;task1&amp;quot;&lt;/span&gt;, column);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;var &lt;/span&gt;model = &lt;span style="color: blue"&gt;new&lt;/span&gt;[] { &lt;span style="color: #2b91af"&gt;Mapper&lt;/span&gt;.Map&amp;lt;&lt;span style="color: #2b91af"&gt;Board&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;BoardViewModel&lt;/span&gt;&amp;gt;(board) };&lt;br /&gt;    &lt;span style="color: blue"&gt;return &lt;/span&gt;View(model);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You can see it just creates a new, hard-coded task board entity, maps it to the BoardViewModel class, and returns it.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To quickly create a View page that will accept this ActionResult, I right-click on the method name “Index” and choose “New View”:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_juzpwZu8gCw/S8FKReuHzgI/AAAAAAAAAEM/Ca6LqB3Gy2s/s1600-h/add-view-context-menu%5B8%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="add-view-context-menu" border="0" alt="add-view-context-menu" src="http://lh6.ggpht.com/_juzpwZu8gCw/S8FKRviGmpI/AAAAAAAAAEQ/7O95czj1nE8/add-view-context-menu_thumb%5B4%5D.png?imgmax=800" width="316" height="66" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This brings up the Add View wizard, in which I can create a new ASPX view file that is strongly typed to my BoardViewModel class, uses the Master page for my project, and puts in some initial code to display the data:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/S8FKSAYxhuI/AAAAAAAAAEU/b6gbYqakQsw/s1600-h/add-view-wizard%5B5%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="add-view-wizard" border="0" alt="add-view-wizard" src="http://lh4.ggpht.com/_juzpwZu8gCw/S8FKSTURg4I/AAAAAAAAAEY/srUJtRZlghU/add-view-wizard_thumb%5B3%5D.png?imgmax=800" width="441" height="436" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I chose “List” as the View content type, because that selection assumes I will be returning an enumerable list of models, and puts a “foreach” loop into the ASPX page (I’ll use that code later as I build up the task board… where I used the ASP Repeater control in the Webforms project, I’ll use “foreach” to create the correct HTML in this MVC view page.&amp;#160; This is pure code-generated quick and dirty programming… which I think is dangerous if you don’t use it correctly, but valuable for quickly understanding the ingredients in an MVC web app and getting started with a screen quickly.&amp;#160; Even though this view of the BoardViewModel doesn’t look anything like a task board yet, I think it is useful as an exercise:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/S8FKSn6HBXI/AAAAAAAAAEc/Lnch7mUMQeE/s1600-h/code-generated-view%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="code-generated-view" border="0" alt="code-generated-view" src="http://lh3.ggpht.com/_juzpwZu8gCw/S8FKTVPcS_I/AAAAAAAAAEg/eAAwKHeJkc4/code-generated-view_thumb%5B2%5D.png?imgmax=800" width="540" height="289" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To see the generated code, check out the HTML in the &lt;a title="Code view of Index.asp in NCharlie code repository" href="http://code.google.com/p/ncharlie/source/browse/tags/2010-0410-mvc-quickstart/src/UI.Mvc/Views/TaskBoard/Index.aspx"&gt;Index.aspx file&lt;/a&gt; that creates this view.&amp;#160; I tagged this code in source control as “&lt;a title="MVC quick start tag in google code repository" href="http://code.google.com/p/ncharlie/source/browse/#svn/tags/2010-0410-mvc-quickstart"&gt;MVC quick start&lt;/a&gt;”.&amp;#160; Next, I’ll do things right and create some tests that define a set of actions and expected behavior for the TaskBoardController.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-4180860247452827117?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/4180860247452827117/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/04/ncharlie-task-board-mvc-version.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/4180860247452827117'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/4180860247452827117'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/04/ncharlie-task-board-mvc-version.html' title='NCharlie Task Board – MVC Version'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_juzpwZu8gCw/S8FKRDEAq4I/AAAAAAAAAEI/3uvtwe7GqZ8/s72-c/UI.Mvc-project-tree_thumb%5B3%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-3351831556805477946</id><published>2010-02-26T22:46:00.001-08:00</published><updated>2010-02-26T22:46:49.392-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NCharlie'/><category scheme='http://www.blogger.com/atom/ns#' term='Webforms'/><title type='text'>NCharlie Task Board – Webforms version</title><content type='html'>&lt;p&gt;…in which the task board UI comes alive.&lt;/p&gt;  &lt;p&gt;I’ve got my domain objects and simple persistence finished.&amp;#160; Now I will implement the user interface first using ASP.Net Webforms.&amp;#160; I created screen mockups in an &lt;a title="NCharlie Project Mockup, Domain Model" href="http://charliesolomon.blogspot.com/2010/02/ncharlie-project-mockup-domain-model.html"&gt;earlier post&lt;/a&gt;, and this view model class is a representation of that screen:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;BoardViewModel&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;public string &lt;/span&gt;Name { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;    &lt;span style="color: blue"&gt;public int &lt;/span&gt;CommunityId { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;    &lt;span style="color: blue"&gt;public int &lt;/span&gt;PageId { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ColumnViewModel&lt;/span&gt;[] Columns { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;    &lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ColumnViewModel&lt;br /&gt;    &lt;/span&gt;{&lt;br /&gt;        &lt;span style="color: blue"&gt;public string &lt;/span&gt;Id { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;        &lt;span style="color: blue"&gt;public string &lt;/span&gt;Name { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TaskViewModel&lt;/span&gt;[] Tasks { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;        &lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TaskViewModel&lt;br /&gt;        &lt;/span&gt;{&lt;br /&gt;            &lt;span style="color: blue"&gt;public string &lt;/span&gt;Id { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;            &lt;span style="color: blue"&gt;public string &lt;/span&gt;Name { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;            &lt;span style="color: blue"&gt;public bool &lt;/span&gt;InFirstColumn { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;            &lt;span style="color: blue"&gt;public bool &lt;/span&gt;InLastColumn { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The user interface for this model is a “board” with a variable number of columns, each of which has a variable number of tasks in it that can jump forward or back to other columns.&amp;#160; For the Webforms version I use nested Repeater controls to accomplish this.&amp;#160; This is how it turned out:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_juzpwZu8gCw/S4i_v8kU9SI/AAAAAAAAAD8/WzoKrsQz_Ns/s1600-h/TaskBoard-Webform01%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="TaskBoard-Webform01" border="0" alt="TaskBoard-Webform01" src="http://lh5.ggpht.com/_juzpwZu8gCw/S4i_wcwjQZI/AAAAAAAAAEA/kgR7Uf2kmgE/TaskBoard-Webform01_thumb%5B2%5D.png?imgmax=800" width="604" height="386" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I’m keeping this post short, but there is actually plenty to talk about behind the scenes that I may cover in more detail in later posts.&amp;#160; For now, here’s a list of elements that in my opinion made this little project fun to work on:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Uses a master page, NCharlie.master&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;  &lt;li&gt;Uses jQuery for showing/hiding textboxes for user input&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;  &lt;li&gt;Uses Automapper to simplify mapping entities retrieved from the database to the view model for display&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;  &lt;li&gt;Uses the StructureMap IoC container for injection of IBoardRepository into the page constructor&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;  &lt;li&gt;Web.config modification: added HttpModule reference for the TransactionBoundaryModule class (implements the Unit of Work pattern using one NHibernate Session Per Http Request)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;There are some things I need to improve… for example, I don’t have StructureMap configured to automatically inject the repository class.&amp;#160; Rather, I’m asking StructureMap for it explicitly in the constructor… simply because I’m still weak in that area and need to learn how.&amp;#160; Also, I skipped writing tests for this bit of code… mainly because I find it hard to write tests for Webform controller classes.&amp;#160; Hopefully I will be able to fix that when I implement the MVC version.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Check it out in the &lt;a title="NCharlie on Google Code - Webform tagged branch" href="http://code.google.com/p/ncharlie/source/browse/#svn/tags/2010-0226-webforms"&gt;Webform branch&lt;/a&gt; of the NCharlie source.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-3351831556805477946?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/3351831556805477946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/02/ncharlie-task-board-webforms-version.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/3351831556805477946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/3351831556805477946'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/02/ncharlie-task-board-webforms-version.html' title='NCharlie Task Board – Webforms version'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_juzpwZu8gCw/S4i_wcwjQZI/AAAAAAAAAEA/kgR7Uf2kmgE/s72-c/TaskBoard-Webform01_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-2131572358414549457</id><published>2010-02-18T22:59:00.001-08:00</published><updated>2010-02-18T23:00:31.769-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NCharlie'/><category scheme='http://www.blogger.com/atom/ns#' term='Fluent NHibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>NCharlie – Simple Persistence with Fluent NHibernate</title><content type='html'>&lt;p&gt;Now that I have the domain objects (Board, Column and Task) built up and tested, it is time to wire them up to a database.&amp;#160; I want to create them, click Save, go home and play Wii with my boys… then come back the next day to find them persisted, waiting for me to work with them again.&lt;/p&gt;  &lt;p&gt;I’m going to keep this part &lt;em&gt;really&lt;/em&gt; simple… because it should be.&amp;#160; The days of writing stored procedures, or ADO.Net connection, command, execution and mapping code are dead to me.&amp;#160; At least here in the NCharlie project they are!&lt;/p&gt;  &lt;p&gt;First, I’ll give credit where credit is due… The folks at Headspring have contributed &lt;em&gt;lots&lt;/em&gt; of code to the community through open source projects like &lt;a title="CodeCampServer on Google Code" href="http://code.google.com/p/codecampserver/"&gt;CodeCampServer&lt;/a&gt;.&amp;#160; The following simple data access code, and most of the NCharlie starter project, comes from the example project &lt;a title="Jimmy Bogard&amp;#39;s blog on Los Techies" href="http://www.lostechies.com/blogs/jimmy_bogard/default.aspx"&gt;Jimmy Bogard&lt;/a&gt; used at his MVC boot camp course in January.&amp;#160; Check out the NCharlie source to see the configuration files and base classes that make the following simple mapping code possible.&lt;/p&gt;  &lt;p&gt;Here’s a test showing that I want to persist a Board entity to a database:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_juzpwZu8gCw/S3423W_QHWI/AAAAAAAAADo/JvtXXbgdIRI/s1600-h/board-map-test%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="board-map-test" border="0" alt="board-map-test" src="http://lh3.ggpht.com/_juzpwZu8gCw/S3423vbKR8I/AAAAAAAAADs/l4na06cYbkw/board-map-test_thumb%5B2%5D.png?imgmax=800" width="598" height="223" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Here’s a mapping class that makes the test pass:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/S34230jZ1DI/AAAAAAAAADw/CzgXSHHb4wY/s1600-h/board-map%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="board-map" border="0" alt="board-map" src="http://lh4.ggpht.com/_juzpwZu8gCw/S3424By99KI/AAAAAAAAAD0/vylCNQTAWhA/board-map_thumb%5B2%5D.png?imgmax=800" width="383" height="165" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;That’s all that’s needed to get the Board entity saved.&amp;#160; I really like the simple syntax used here… no strings like “Name” or “CommunityId” in this code.&lt;/p&gt;  &lt;p&gt;I wanted to keep it short and simple here… I did add some other persistence features (cascading updates/deletes, joins.)&amp;#160; You can check out the code (which includes mapping code for the other entities) in the source branch tagged &lt;a title="Persistence branch on Google Code" href="http://code.google.com/p/ncharlie/source/browse/#svn/tags/2010-0218-persistence"&gt;2010-0218-persistence&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/1240921806131117404-2131572358414549457?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/2131572358414549457/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/02/ncharlie-simple-persistence-with-fluent.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2131572358414549457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2131572358414549457'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/02/ncharlie-simple-persistence-with-fluent.html' title='NCharlie – Simple Persistence with Fluent NHibernate'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_juzpwZu8gCw/S3423vbKR8I/AAAAAAAAADs/l4na06cYbkw/s72-c/board-map-test_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-985334200353771326</id><published>2010-02-15T10:48:00.001-08:00</published><updated>2010-02-15T10:52:11.398-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NCharlie'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>NCharlie Project Mockup, Domain Model</title><content type='html'>&lt;p&gt;Here are the mockups of the two project screens I will focus on in the next few posts.&amp;#160; First, here is a the page that allows users to create a new task board:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_juzpwZu8gCw/S3mXBzBga1I/AAAAAAAAADI/jsL4EoYWROE/s1600-h/TaskBoard0210.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="TaskBoard02" border="0" alt="TaskBoard02" src="http://lh5.ggpht.com/_juzpwZu8gCw/S3mXCb-2mCI/AAAAAAAAADM/j5Hv3wS8RTU/TaskBoard02_thumb8.png?imgmax=800" width="402" height="44" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I will want this application to allow users to set up multiple task boards for different teams/groups.&amp;#160; Since I’d like to use this software at work someday, each task board will have a unique CommunityId and PageId so they can reside in a portal application.&amp;#160; Once a new task board is created, columns and tasks may be added to it:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/S3mXCn_n85I/AAAAAAAAADQ/S3A5xX41L9g/s1600-h/TaskBoard015.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="TaskBoard01" border="0" alt="TaskBoard01" src="http://lh4.ggpht.com/_juzpwZu8gCw/S3mXC2kzDeI/AAAAAAAAADU/MqdaWX_KfBM/TaskBoard01_thumb3.png?imgmax=800" width="602" height="348" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;A task can move forward and backward through the columns.&amp;#160; Columns and tasks may be deleted.&lt;/p&gt;  &lt;p&gt;The problem domain contains Boards, Columns and Tasks.&lt;/p&gt;  &lt;p&gt;This set of user stories describes the functionality of the task board:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;When adding a new Board, the Board Name, CommunityId and PageId should be specified.&lt;/li&gt;    &lt;li&gt;When adding a new Column to a Board, the Column name should be specified. &lt;/li&gt;    &lt;li&gt;When adding a new Task, a Task description should be specified. &lt;/li&gt;    &lt;li&gt;When displaying a Board, all the Board’s columns should be shown. &lt;/li&gt;    &lt;li&gt;A new Column should be added to the end of the Board’s list of columns. &lt;/li&gt;    &lt;li&gt;A Column may be deleted. &lt;/li&gt;    &lt;li&gt;When displaying a Column on a Board, all the Column’s Tasks should be shown. &lt;/li&gt;    &lt;li&gt;A Task may Task should be added to the end of the Column’s list of Tasks. &lt;/li&gt;    &lt;li&gt;A Task may be moved from its Column to an adjacent Column. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;I wrote unit tests for the non-trivial stories above, and created domain objects and methods to make them pass:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/S3mXDTlUAfI/AAAAAAAAADY/KR5BtDJwPOI/s1600-h/domain-tests%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="domain-tests" border="0" alt="domain-tests" src="http://lh4.ggpht.com/_juzpwZu8gCw/S3mXDmIgRGI/AAAAAAAAADc/yhotgtVjzRs/domain-tests_thumb%5B1%5D.png?imgmax=800" width="504" height="250" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Here is the class diagram for this simple domain:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/S3mXD2jWxRI/AAAAAAAAADg/uGoN08juJMQ/s1600-h/class-diagram%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="class-diagram" border="0" alt="class-diagram" src="http://lh6.ggpht.com/_juzpwZu8gCw/S3mXEcQzhJI/AAAAAAAAADk/Zj377dNpUMw/class-diagram_thumb%5B1%5D.png?imgmax=800" width="504" height="277" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I tagged the code for this post in source control, you can check it out &lt;a title="NCharlie domain unit tests on Google Code" href="http://code.google.com/p/ncharlie/source/browse/#svn/tags/2010-0215-domain-tests"&gt;here&lt;/a&gt;.&amp;#160; Next I’ll walk through the data access code… Or I’ll create view models and UI elements… Or I’ll step back and explain some patterns in the NCharlie starter project… I’ll have to surprise you because I don’t know yet!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-985334200353771326?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/985334200353771326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/02/ncharlie-project-mockup-domain-model.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/985334200353771326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/985334200353771326'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/02/ncharlie-project-mockup-domain-model.html' title='NCharlie Project Mockup, Domain Model'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_juzpwZu8gCw/S3mXCb-2mCI/AAAAAAAAADM/j5Hv3wS8RTU/s72-c/TaskBoard02_thumb8.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-7092842063631689857</id><published>2010-02-14T01:24:00.001-08:00</published><updated>2010-02-14T01:29:04.968-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVC'/><category scheme='http://www.blogger.com/atom/ns#' term='NCharlie'/><category scheme='http://www.blogger.com/atom/ns#' term='Webforms'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><title type='text'>Resurrecting NCharlie</title><content type='html'>&lt;p&gt;We delivered some software at my day job last week that has been keeping me pretty busy for the past few weeks.&amp;#160; To celebrate, I am revving up the &lt;a title="NCharlie project on Google Code" href="http://code.google.com/p/ncharlie/"&gt;NCharlie project&lt;/a&gt; again to buzz out some things I picked up in Austin last month.&lt;/p&gt;  &lt;p&gt;I’ve already spent some time integrating &lt;a title="StructureMap on SourceForge" href="http://structuremap.sourceforge.net"&gt;StructureMap&lt;/a&gt;, &lt;a title="Automapper" href="http://automapper.codeplex.com/"&gt;Automapper&lt;/a&gt;, and &lt;a title="Fluent NHibernate" href="http://fluentnhibernate.org/"&gt;Fluent NHibernate&lt;/a&gt; into a task manager Webforms application that uses the patterns I learned last month in bootcamp.&amp;#160; Here’s what I’ve got so far:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_juzpwZu8gCw/S3fBM_NCAKI/AAAAAAAAADA/V3SV_DE6yaM/s1600-h/NCharlie-TaskBoard%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="NCharlie-TaskBoard" border="0" alt="NCharlie-TaskBoard" src="http://lh4.ggpht.com/_juzpwZu8gCw/S3fBNAEIxUI/AAAAAAAAADE/mKhXYwL_8sA/NCharlie-TaskBoard_thumb%5B2%5D.png?imgmax=800" width="539" height="381" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;We’ve been using a “task board” like this at work to move our development tasks through to deployment.&lt;/p&gt;  &lt;p&gt;I know, the CSS needs some work… but the thing is working!&amp;#160; Here’s what I’ve done so far:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Started a new blank solution for NCharlie in Visual Studio 2008.&lt;/li&gt;    &lt;li&gt;Added .Net 3.5 projects for the Core NCharlie project, a Webforms UI project, an MVC UI project (using &lt;a title="Phil Haack&amp;#39;s ASP.Net MVC 2 RC 2 Announcement" href="http://haacked.com/archive/2010/02/04/aspnetmvc2-rc2.aspx"&gt;ASP.Net MVC 2 RC 2&lt;/a&gt;), Integration and Unit Test projects.&lt;/li&gt;    &lt;li&gt;I put the blank NCharlie starter project (no entities, tests, views… just the architecture) into a branch tagged &lt;a title="NCharlie Starter project on Google Code" href="http://code.google.com/p/ncharlie/source/browse/#svn/tags/ncharlie-starter"&gt;ncharlie-starter&lt;/a&gt;.&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;As of tonight, the version in the trunk of the NCharlie repository matches the starter project.&amp;#160; My plan is to build it up using a branch per feature, and blog about it as I go.&amp;#160; I plan to mirror the functionality using Webforms and the MVC framework.&amp;#160; I think it will be a good way for me to cut my teeth on MVC, plus it will be interesting to compare the two when it is done.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-7092842063631689857?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/7092842063631689857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/02/resurrecting-ncharlie.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/7092842063631689857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/7092842063631689857'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/02/resurrecting-ncharlie.html' title='Resurrecting NCharlie'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_juzpwZu8gCw/S3fBNAEIxUI/AAAAAAAAADE/mKhXYwL_8sA/s72-c/NCharlie-TaskBoard_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-2361097207465099146</id><published>2010-01-15T15:22:00.000-08:00</published><updated>2010-01-15T16:54:41.870-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVC'/><category scheme='http://www.blogger.com/atom/ns#' term='Training'/><title type='text'>MVC Bootcamp takeaway</title><content type='html'>I just finished three days of &lt;a href="http://headspringsystems.com/"&gt;Headspring MVC bootcamp&lt;/a&gt; training with &lt;a href="http://www.lostechies.com/blogs/jimmy_bogard/"&gt;Jimmy Bogard&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Impressions:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Great training, I would highly recommend anything these guys offer.  I enjoy a fast pace, hands on (lots of programming exercises) style of training, and that's what I got.  Jimmy is a good teacher... I already knew that, since I've been reading his blog for the past year or so and I've picked up a lot a good pointers to things that help me in my work.  He is also a good communicator teaching in front of a room full of diverse programmers.  If you are new to MVC like I am, this course is great for introducing concepts that may be new to web forms programmers... but like the &lt;a href="http://www.amazon.com/ASP-Net-MVC-Action-Jeffrey-Palermo/dp/1933988622"&gt;MVC book&lt;/a&gt; that Jimmy co-authored, the class goes way beyond the introduction by showing how they leverage some of the features in MVC (ActionFilters and ResultFilters, HtmlTemplates, Model binding and validation...) to build applications quickly that are easier to maintain.  We used the release candidate of &lt;a href="http://weblogs.asp.net/scottgu/archive/2010/01/10/asp-net-mvc-2.aspx"&gt;ASP.Net MVC 2&lt;/a&gt; in class.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;We use ASP.Net web forms at work, and I don't think we will change to MVC in the near future.  Maybe at some point in the next year - I'm not sure yet.  I knew that going into this class, but I also hoped I would learn about valuable patterns and tools that we can use right now (yes, even in web form development) that will make us go faster or that will make our apps easier to maintain.  My hopes were not dashed... here is my list of "valuables" (this list is more for me and my team... but if someone else gets something out of it, great!):&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Model Binding - this seemed magic to me at first, and comes for free in the MVC framework.  When a form posts to the server, the action method ("event handler" for us web form devs) that accepts the post takes the payload in as a parameter... which is &lt;em&gt;already&lt;/em&gt; mapped to a model class.  No tedious "model.FirstName = txtFirstName.Text" code to write.  This would be nice to have in our work... there may already be something similar in web form development that I just don't know about.  If not, I don't think it would be much work to write some convention-based code that does this type of work for us.&lt;/li&gt;&lt;li&gt;ViewModel &lt;a href="http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx"&gt;validation using property attributes&lt;/a&gt; - we write a lot of repetitive code that validates email addresses, phone numbers, department numbers, etc.  It would be nice to be able to put an attribute on a property in our ViewModel class (for example, "[EmailAddress]" and have the form post automatically check these for us.&lt;/li&gt;&lt;li&gt;Html builders - the RC version of ASP.Net MVC 2 has &lt;a href="http://weblogs.asp.net/scottgu/archive/2010/01/10/asp-net-mvc-2-strongly-typed-html-helpers.aspx"&gt;helper methods&lt;/a&gt; like "Html.EditorFor(x =&gt; x.FirstName)" which spit out the label, text box, and validation message element for a property called "FirstName" on a view model.  We do something similar with user controls for our pages... but we could do a lot better, and the implementation in MVC gives me some ideas.  MVC 2 even has an "Html.EditorForModel()" method that will render the entire collection of form controls needed for a particular view model.  Handy!&lt;/li&gt;&lt;li&gt;Use an IoC container like StructureMap - we have been using &lt;a href="http://www.lostechies.com/blogs/chad_myers/archive/2009/07/14/just-say-no-to-poor-man-s-dependency-injection.aspx"&gt;poor man's dependency injection&lt;/a&gt; for a few months now, but we need to step it up and use the factory pattern to do the heavy lifting for us... if a presenter/controller class needs an IPersonRepository, get it from the factory.  Stop explicitly injecting it when the controller is requested.&lt;/li&gt;&lt;li&gt;Use &lt;a href="http://automapper.codeplex.com/"&gt;AutoMapper&lt;/a&gt; to map domain models to view models.  Jimmy is right, mapping code is tedious to write.&lt;/li&gt;&lt;li&gt;Move to NHibernate.  I've been gently moving our team away from classic data access layers with ADO.Net for the past year.  We use a really simple, in-house ORM that I wrote (yes, I know... I re-invented the wheel - but it seemed the best way to transition) that reduces the number of stored procedures per project by ~90%.  It is time to go all the way, so we can realize the performance, maintainability, and code-quality benefits NHibernate will bring.&lt;/li&gt;&lt;li&gt;Lighten up our presenter/controller classes.  These are still pretty heavy in our team... something Jimmy showed us, and I will be looking up in CodeCampServer, was using the Strategy pattern to execute commands when a form requests something.  For example, a form posts to the server and needs to save data... CodeCampServer uses an "ExecuteCommand" method to pass the ViewModel payload, and the domain entity type, to another class that handles the work of mapping and persisting.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I'm sure I am forgetting something... I took good notes though that I'm sure will be printed and worn out over the next months.  Thanks for a great class Jimmy!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-2361097207465099146?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/2361097207465099146/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/01/mvc-bootcamp-takeaway.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2361097207465099146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2361097207465099146'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/01/mvc-bootcamp-takeaway.html' title='MVC Bootcamp takeaway'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-38728706826178143</id><published>2010-01-07T03:35:00.000-08:00</published><updated>2010-01-07T04:16:45.590-08:00</updated><title type='text'>Bootcamp 2010</title><content type='html'>I will be attending a 3 day &lt;a href="http://www.headspringsystems.com/services/agile-training/mvc-training/"&gt;bootcamp&lt;/a&gt; on ASP.Net MVC in Austin next week, taught by &lt;a href="http://www.lostechies.com/blogs/jimmy_bogard/archive/2010/01/06/mvc-bootcamp-next-week.aspx"&gt;Jimmy Bogard&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It has been almost a year since I took Headspring's Agile .Net Bootcamp (March 2009, taught by &lt;a href="http://mhinze.com/"&gt;Matt Hinze&lt;/a&gt;).  We are still using web forms where I work, but we have adopted many of the tools/patterns/practices I learned about last year (Unit testing, using an ORM, SOLID principles).  My prioritized notes from bootcamp 2009 of "what to implement back at work" are here on my desk... I refer back to them often.&lt;br /&gt;&lt;br /&gt;This year, I'm looking forward to Jimmy's presentation on how he uses the MVC framework... we will most likely adopt MVC later this year.  But I'm also hoping to come back a little stronger, or at least with more information awareness, in some areas I know I'm weak in.  These are not necessarily listed in the course description, but from what I've seen in the Headspring blogosphere and CodeCampServer, these are integral parts of their projects:&lt;br /&gt;&lt;br /&gt;- Inversion of Control (IOC)&lt;br /&gt;- Unit of Work pattern and how it works with a web request&lt;br /&gt;- Basic usage of AutoMapper?&lt;br /&gt;- Better design in general (I still build pretty heavy controllers)&lt;br /&gt;&lt;br /&gt;And, of course I'm looking forward to seeing Austin again and having a slice of &lt;a href="http://en.wikipedia.org/wiki/Bevo_%28mascot%29"&gt;Bevo&lt;/a&gt; for dinner one night!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-38728706826178143?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/38728706826178143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2010/01/bootcamp-2010.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/38728706826178143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/38728706826178143'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2010/01/bootcamp-2010.html' title='Bootcamp 2010'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-2975689511362567237</id><published>2009-07-18T11:14:00.001-07:00</published><updated>2009-07-18T11:14:22.114-07:00</updated><title type='text'>Just fix it!</title><content type='html'>&lt;p&gt;It’s Saturday.&amp;#160; I just spent an hour and a half outside a Starbucks killing off open project items for an old client.&amp;#160; It was hot outside and the friendly folks enjoying their Saturday were starting to distract me… so now I’m at the library.&lt;/p&gt;  &lt;p&gt;I like developing software – even on Saturday.&amp;#160; There are other things I like doing more (especially on Saturday, especially if it involves my wife and kids.)&amp;#160; Developing software isn’t the worst thing I could be doing, but this particular software is trying my patience.&lt;/p&gt;  &lt;p&gt;This software is a giant beast of business critical software that people are counting on.&amp;#160; It needs to be functional soon so the legacy system can rest in peace.&amp;#160; This software project put me out of business (I guess that’s not &lt;em&gt;totally&lt;/em&gt; fair… my 5-year-ago self’s lack of project management skills did me in – but this software is the blob in front of me that points that out.)&lt;/p&gt;  &lt;p&gt;Why am I writing about this instead of “Just fixing it”?&amp;#160; Good question.&amp;#160; Answer: I don’t know how to fix a lot of the open items that are left.&amp;#160; The original requirements analyst did not give me a good set of requirements explaining how the system should work (btw, I was the original analyst.&amp;#160; And developer.&amp;#160; And accountant.&amp;#160; And salesman…)&lt;/p&gt;  &lt;p&gt;Now there, I got that off my chest and onto the internet – I feel much better.&amp;#160; Now, back to work… This is not an &lt;a title="Intractable - Wikipedia" href="http://en.wikipedia.org/wiki/Intractable#Intractability"&gt;intractable&lt;/a&gt; problem, but I can’t program my way out of this one without some help.&lt;/p&gt;  &lt;p&gt;Some software problems are easy to solve.&amp;#160; For example: “The feature rate isn’t getting populated when I add a feature to the rental agreement.”&amp;#160; I can fix that.&lt;/p&gt;  &lt;p&gt;Some problems are harder, even impossible, without a better story behind them.&amp;#160; For example: “The rental agreements are billing 10 days”.&amp;#160; I can’t just fix that.&amp;#160; I get the implication here… rental agreements should not always “bill 10 days”.&amp;#160; How should they bill?&amp;#160; Are there some cases where 10 days is appropriate?&amp;#160; The questions go on and on…&lt;/p&gt;  &lt;p&gt;This is not at all meant to belittle the user who reported this issue.&amp;#160; He saw something wrong and reported it, nothing wrong with that.&amp;#160; My point is that I have a lot of non-programming work to do before I can begin to solve such a problem.&amp;#160; I need to expand this user’s ~10 word problem report into a series of stories that look like this:&lt;/p&gt;  &lt;p&gt;“When a user does X, the software should respond with Y.”&lt;/p&gt;  &lt;p&gt;A more specific, contrived example: “When a rental agreement is created with Usage type D, it should use the Daily Rate and should not be included in the ready to Invoice List until the next Friday after it was created.”&lt;/p&gt;  &lt;p&gt;Once I have a complete set of those, then I can fix it.&amp;#160; Since I’m a proactive kind of guy, I’ll spend this Saturday expanding this one issue into lots and lots of short phrases (user stories) like the one above.&amp;#160; It isn’t the most satisfying work… especially since it will probably take a while for my client to confirm, deny or expand my list of stories.&amp;#160; But c'est la vie…&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-2975689511362567237?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/2975689511362567237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/07/just-fix-it.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2975689511362567237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2975689511362567237'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/07/just-fix-it.html' title='Just fix it!'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-6288417416855685311</id><published>2009-06-11T23:09:00.001-07:00</published><updated>2010-02-13T22:16:14.310-08:00</updated><title type='text'>Side by side code: Classic ADO.Net and the NHibernate ORM</title><content type='html'>&lt;p&gt;This post continues my “NCharlie” post series, in which I take Agile methods/technologies I picked up at a .Net Bootcamp in Austin back to work.  My intended audience is my future self (reference) and my coworkers – we write custom business apps using .Net 2.0 webforms backed by SQL Server.&lt;/p&gt;&lt;p&gt;Currently our team maintains &lt;em&gt;lots&lt;/em&gt; of SQL stored procedures per application.  As our lead developer I would like to lead the team in a new direction that doesn’t include all these sprocs.  The benefits of such a move are numerous and &lt;a title="Jeremy Miller MSDN article on Unit of Work and Persistence Ignorance" href="http://msdn.microsoft.com/en-us/magazine/dd882510.aspx"&gt;well-stated&lt;/a&gt; (edited, updated incorrect link) by people much smarter and more experienced than me.  But for us, the immediate value would be: &lt;/p&gt;&lt;p&gt;1) Less code to maintain (yes, Transact-SQL is code).&lt;/p&gt;&lt;p&gt;2) &lt;em&gt;Persistence ignorance&lt;/em&gt;, resulting in code that is more testable and easier to create, understand later, and fix if needed.  An SQL stored procedure is a terrible place for business logic to reside (hard to test, hard to find)… removing the temptation to put it there is a hidden benefit.&lt;/p&gt;&lt;p&gt;3) Faster development cycles.  We’ve built this CRUD over and over again so many times.  Let’s be pragmatic and stop repeating ourselves.  Even with code generators the CRUD procedures and data access layers are a painful part of our projects we shouldn’t be worrying about.&lt;/p&gt;&lt;p&gt;I could probably go on… instead I’ll jump into an example.  You can find this type of example code in &lt;em&gt;many&lt;/em&gt; other places, I’m not reinventing anything here.  Hopefully this side by side format will help someone else out there move on from writing the type of data access code I’ve been writing for too many years now.&lt;/p&gt;&lt;p&gt;First up let’s knock the “C” off the classic ADO.Net CRUD.  “C” is for &lt;em&gt;create&lt;/em&gt;, and I want to create a record in my database that corresponds to an entity class in my application I’ve populated with data.  The entity may have been populated by taking a user’s inputs on a web form and mapping them to the entity’s properties… it doesn’t matter where I got the data – I’m concentrating now on persisting it to the datastore.  Here are the steps involved in such a task:&lt;/p&gt;&lt;p&gt;1) Write a stored procedure that takes input parameters corresponding to the entity properties I want to save, executes an INSERT statement against the proper table, and returns an identifier for the new record. (note: we use a code generator against a database table to create this procedure)&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;CREATE PROCEDURE &lt;/span&gt;[dbo]&lt;span style="color:gray;"&gt;.&lt;/span&gt;[InsertTask]&lt;br /&gt;    @Description &lt;span style="color:blue;"&gt;varchar&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;500&lt;span style="color:gray;"&gt;),&lt;br /&gt;    &lt;/span&gt;@Submitted &lt;span style="color:blue;"&gt;datetime&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:green;"&gt;&lt;br /&gt;    &lt;/span&gt;@SubmittedBy &lt;span style="color:blue;"&gt;varchar&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;50&lt;span style="color:gray;"&gt;),&lt;br /&gt;    &lt;/span&gt;@Closed &lt;span style="color:blue;"&gt;datetime&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;@ClosedBy &lt;span style="color:blue;"&gt;varchar&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;50&lt;span style="color:gray;"&gt;),&lt;br /&gt;&lt;/span&gt;&lt;span style="color:gray;"&gt;    &lt;/span&gt;@Priority &lt;span style="color:blue;"&gt;int&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;@AssignedTo &lt;span style="color:blue;"&gt;varchar&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;50&lt;span style="color:gray;"&gt;)&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;AS&lt;br /&gt;&lt;br /&gt;INSERT INTO &lt;/span&gt;[dbo]&lt;span style="color:gray;"&gt;.&lt;/span&gt;[wdtasks_Tasks] &lt;span style="color:gray;"&gt;(&lt;br /&gt;    &lt;/span&gt;[Description]&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;[Submitted]&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;[SubmittedBy]&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;[Closed]&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;[ClosedBy]&lt;span style="color:gray;"&gt;,&lt;br /&gt;&lt;/span&gt;&lt;span style="color:gray;"&gt;    &lt;/span&gt;[Priority]&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;[AssignedTo]&lt;br /&gt;&lt;span style="color:gray;"&gt;) &lt;/span&gt;&lt;span style="color:blue;"&gt;VALUES &lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;br /&gt;    &lt;/span&gt;@Description&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;@Submitted&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;@SubmittedBy&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;@Closed&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;@ClosedBy&lt;span style="color:gray;"&gt;,&lt;br /&gt;&lt;/span&gt;&lt;span style="color:gray;"&gt;    &lt;/span&gt;@Priority&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;@AssignedTo&lt;br /&gt;&lt;span style="color:gray;"&gt;)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT &lt;/span&gt;&lt;span style="color:magenta;"&gt;SCOPE_IDENTITY&lt;/span&gt;&lt;span style="color:gray;"&gt;()&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;2) We use a data helper class (cleverly named &lt;em&gt;DataHelper)&lt;/em&gt; to instantiate a connection object and an ADO.Net command object.  Then we build up the input parameters and execute a stored procedure.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;string &lt;/span&gt;cn = ConfigurationManager.AppSettings.Get(&lt;span style="color:maroon;"&gt;"dbConnection_Tasks"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;SqlParameter[] aParams = &lt;span style="color:blue;"&gt;new &lt;/span&gt;SqlParameter[7];&lt;br /&gt;            aParams[0] = &lt;span style="color:blue;"&gt;new &lt;/span&gt;SqlParameter(&lt;span style="color:maroon;"&gt;"@Description"&lt;/span&gt;, SqlDbType.VarChar);&lt;br /&gt;            aParams[0].Value = Null.GetNull(oEntity.Description);&lt;br /&gt;            aParams[1] = &lt;span style="color:blue;"&gt;new &lt;/span&gt;SqlParameter(&lt;span style="color:maroon;"&gt;"@Submitted"&lt;/span&gt;, SqlDbType.DateTime);&lt;br /&gt;            aParams[1].Value = Null.GetNull(oEntity.Submitted);&lt;br /&gt;            aParams[2] = &lt;span style="color:blue;"&gt;new &lt;/span&gt;SqlParameter(&lt;span style="color:maroon;"&gt;"@SubmittedBy"&lt;/span&gt;, SqlDbType.VarChar);&lt;br /&gt;            aParams[2].Value = Null.GetNull(oEntity.SubmittedBy);&lt;br /&gt;            aParams[3] = &lt;span style="color:blue;"&gt;new &lt;/span&gt;SqlParameter(&lt;span style="color:maroon;"&gt;"@Closed"&lt;/span&gt;, SqlDbType.DateTime);&lt;br /&gt;            aParams[3].Value = Null.GetNull(oEntity.Closed);&lt;br /&gt;            aParams[4] = &lt;span style="color:blue;"&gt;new &lt;/span&gt;SqlParameter(&lt;span style="color:maroon;"&gt;"@ClosedBy"&lt;/span&gt;, SqlDbType.VarChar);&lt;br /&gt;            aParams[4].Value = Null.GetNull(oEntity.ClosedBy);&lt;br /&gt;            aParams[5] = &lt;span style="color:blue;"&gt;new &lt;/span&gt;SqlParameter(&lt;span style="color:maroon;"&gt;"@Priority"&lt;/span&gt;, SqlDbType.Int);&lt;br /&gt;            aParams[5].Value = Null.GetNull(oEntity.Priority);&lt;br /&gt;            aParams[6] = &lt;span style="color:blue;"&gt;new &lt;/span&gt;SqlParameter(&lt;span style="color:maroon;"&gt;"@AssignedTo"&lt;/span&gt;, SqlDbType.VarChar);&lt;br /&gt;            aParams[6].Value = Null.GetNull(oEntity.AssignedTo);&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;return int&lt;/span&gt;.Parse(DataHelper.ExecuteProcedureScalar(cn, &lt;span style="color:maroon;"&gt;"InsertTask"&lt;/span&gt;, aParams).ToString());&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;After executing the command, we receive a scalar value (the new record identifier) in return.  Then we dispose of the command object and close the connection. (note: our DataHelper class handles the repetitive code here too.)&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Wow, that wasn’t so bad huh?  Code generation and that DataHelper really helps!  It is a good thing it does too, because those 5 steps have to be repeated over, and over, ad nauseam, for ALL the entities that need to be persisted to the database.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Object Relational Mapping to the rescue… let’s perform that same task (saving our entity to the database) using NHibernate:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;1) Create an XML mapping file for the entity.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;&amp;lt;?&lt;/span&gt;&lt;span style="color:maroon;"&gt;xml &lt;/span&gt;&lt;span style="color:red;"&gt;version&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;1.0&lt;/span&gt;" &lt;span style="color:red;"&gt;encoding&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;utf-8&lt;/span&gt;" &lt;span style="color:blue;"&gt;?&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;hibernate-mapping &lt;/span&gt;&lt;span style="color:red;"&gt;xmlns&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;urn:nhibernate-mapping-2.2&lt;/span&gt;"&lt;br /&gt;                  &lt;span style="color:red;"&gt;assembly&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;NCharlie.Core&lt;/span&gt;"&lt;br /&gt;                  &lt;span style="color:red;"&gt;namespace&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;NCharlie.Core.Domain.Model&lt;/span&gt;"&lt;span style="color:blue;"&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;class &lt;/span&gt;&lt;span style="color:red;"&gt;name&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;Task&lt;/span&gt;"&lt;span style="color:blue;"&gt;&amp;gt;&lt;br /&gt;   &lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;id &lt;/span&gt;&lt;span style="color:red;"&gt;name&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;ID&lt;/span&gt;" &lt;span style="color:red;"&gt;column&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;ID&lt;/span&gt;" &lt;span style="color:red;"&gt;type&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;Guid&lt;/span&gt;"&lt;span style="color:blue;"&gt;&amp;gt;&lt;br /&gt;      &amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;generator &lt;/span&gt;&lt;span style="color:red;"&gt;class&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;guid.comb&lt;/span&gt;"&lt;span style="color:blue;"&gt;/&amp;gt;&lt;br /&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color:maroon;"&gt;id&lt;/span&gt;&lt;span style="color:blue;"&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;property &lt;/span&gt;&lt;span style="color:red;"&gt;name&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;Description&lt;/span&gt;" &lt;span style="color:blue;"&gt;/&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;bag &lt;/span&gt;&lt;span style="color:red;"&gt;name&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;Tags&lt;/span&gt;" &lt;span style="color:red;"&gt;access&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;field.camelcase-underscore&lt;/span&gt;" &lt;span style="color:red;"&gt;cascade&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;all-delete-orphan&lt;/span&gt;" &lt;span style="color:blue;"&gt;&amp;gt;&lt;br /&gt;      &amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;key &lt;/span&gt;&lt;span style="color:red;"&gt;column&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;Task_ID&lt;/span&gt;"  &lt;span style="color:blue;"&gt;/&amp;gt;&lt;br /&gt;      &amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;one-to-many &lt;/span&gt;&lt;span style="color:red;"&gt;class&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;Tag&lt;/span&gt;" &lt;span style="color:blue;"&gt;/&amp;gt;&lt;br /&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color:maroon;"&gt;bag&lt;/span&gt;&lt;span style="color:blue;"&gt;&amp;gt;&lt;br /&gt;   &lt;br /&gt;  &amp;lt;/&lt;/span&gt;&lt;span style="color:maroon;"&gt;class&lt;/span&gt;&lt;span style="color:blue;"&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:maroon;"&gt;hibernate-mapping&lt;/span&gt;&lt;span style="color:blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;2) Call the “SaveOrUpdate” method.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;using &lt;/span&gt;(&lt;span style="color:teal;"&gt;ISession &lt;/span&gt;session = GetSession())&lt;br /&gt;&lt;span style="color:blue;"&gt;using &lt;/span&gt;(&lt;span style="color:teal;"&gt;ITransaction &lt;/span&gt;tx = session.BeginTransaction())&lt;br /&gt;{&lt;br /&gt;    session.SaveOrUpdate(entity);&lt;br /&gt;    tx.Commit();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Some of you may be looking at that mapping file and thinking I bumped my head… kind of seems like I’m introducing more complexity if you are used to plain old SQL.  And I am.  But it is a good tradeoff… because once you get that mapping file done, you also get the RUD parts of CRUD for free, as well as a &lt;em&gt;ton&lt;/em&gt; of other NHibernate features (including custom queries, paging…)  The other methods (to round out the CRUD feature) are “List”, “Get” and “Delete”.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The code in step 2 above, the code using the NHibernate ORM, is actually really nice and simple to use once you get used to it.  You can see it in action in the &lt;a title="NCharlie on Google Code" href="http://code.google.com/p/ncharlie/source/browse/#svn/trunk"&gt;NCharlie&lt;/a&gt; example project… and if you work with me, you’ll see it in the near future in the prototype stage of one of our next projects :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-6288417416855685311?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/6288417416855685311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/06/side-by-side-code-classic-adonet-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/6288417416855685311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/6288417416855685311'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/06/side-by-side-code-classic-adonet-and.html' title='Side by side code: Classic ADO.Net and the NHibernate ORM'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-1353829061803087506</id><published>2009-06-11T22:24:00.001-07:00</published><updated>2009-06-11T22:24:53.090-07:00</updated><title type='text'>Geo Tracks – 2009 May 30</title><content type='html'>&lt;p&gt;&lt;em&gt;(published this a bit after the fact… big deadline at work was cramping my style!)&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://elegantcode.com/2009/02/19/code-cast-23-jeremy-miller-on-structuremap/"&gt;Elegant Code Cast 23&lt;/a&gt; – Jeremy Miller on the &lt;a href="http://structuremap.sourceforge.net/Default.htm"&gt;StructureMap&lt;/a&gt; IOC tool for .Net, FubuMVC, JQuery, etc.&amp;#160; I’m not a big IOC user (or understander, for that matter)… but I’ll file this away for the future – Jeremy states that an IOC tool helps us construct applications using &lt;em&gt;composition&lt;/em&gt; of objects vs. &lt;em&gt;inheritance&lt;/em&gt;.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.hanselminutes.com/default.aspx?showID=180"&gt;Hanselminutes 162&lt;/a&gt; – Powershell 2.0.&amp;#160; I love my command shell in Windows XP… and it sounds like Powershell, which is &lt;em&gt;built in&lt;/em&gt; to Windows 7, is definitely something to learn at some point.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blog.stackoverflow.com/2009/05/podcast-54/"&gt;StackOverflow Podcast #54&lt;/a&gt; – Bespoke software development (a.k.a. bidding for jobs vs. creating a new product), the “God” algorithm (the one that just knows the correct answer in one operation), URL routing (and the snafus available when URLs conflict with data… eg. a user with username “username”).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://storage.cornerstonesimi.com/sermons/audio/09_119_God_Is_Strong_Am_I_Audio_Podcast.mp3"&gt;God is Strong, Am I?&lt;/a&gt; – Francis Chan from May 10, 2009.&amp;#160; Let’s live by the Spirit people… why do we go through life afraid or weak or ambivalent when the One who made everything dwells in us?&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-1353829061803087506?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/1353829061803087506/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/06/geo-tracks-2009-may-30.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/1353829061803087506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/1353829061803087506'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/06/geo-tracks-2009-may-30.html' title='Geo Tracks – 2009 May 30'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-38913099659954402</id><published>2009-05-23T00:18:00.001-07:00</published><updated>2009-05-23T00:18:38.392-07:00</updated><title type='text'>Revving up the TDD engine</title><content type='html'>&lt;a href="http://lh5.ggpht.com/_juzpwZu8gCw/ShejR1kWv4I/AAAAAAAAACg/z9B0O1_A5FA/s1600-h/tach%5B3%5D.jpg"&gt;&lt;img title="tach" style="border-right: 0px; border-top: 0px; display: inline; margin-left: 0px; border-left: 0px; margin-right: 0px; border-bottom: 0px" height="132" alt="tach" src="http://lh5.ggpht.com/_juzpwZu8gCw/ShejSHkpmYI/AAAAAAAAACk/B8P5YNwnfic/tach_thumb%5B1%5D.jpg?imgmax=800" width="244" align="right" border="0" /&gt;&lt;/a&gt;   &lt;p&gt;I’m still in the early stages of writing unit tests at work… but I did commit 26 passing tests to source control this afternoon!&amp;#160; The &lt;a title="TDD Pain Points" href="http://charliesolomon.blogspot.com/2009/05/tdd-pain-points-retrospective.html"&gt;pain&lt;/a&gt; is fading, I’m gaining confidence and speed and I’m getting excited about the number of tests I’ve written.&amp;#160; We’re no &lt;a title="mature TDD shop" href="http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/04/09/a-sign-of-team-maturity.aspx"&gt;mature TDD shop&lt;/a&gt; yet, but it is an enjoyable stage when the shiny new unit tests still make us go “ah”.&lt;/p&gt;  &lt;p&gt;In this post I’ll finish out the TDD section of my NCharlie project series by walking through some code that tests the interaction between a controller and view.&amp;#160; It includes using Rhino Mocks to raise an event and verify that the controller calls the correct method on the view in response.&amp;#160; I know in my &lt;a href="http://charliesolomon.blogspot.com/2009/05/do-we-have-time-to-write-tests-on-our.html"&gt;last post&lt;/a&gt; I practically gave up on Rhino Mocks in favor of writing my own mock classes and methods… well, I changed my mind.&amp;#160; RM is a great tool, and I’m getting used to the syntax required for .Net 2.0 projects.&amp;#160; So here goes…&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_juzpwZu8gCw/ShejScJFv3I/AAAAAAAAACo/oSyv54ki4xU/s1600-h/search-results%5B3%5D.jpg"&gt;&lt;img title="search-results" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="137" alt="search-results" src="http://lh5.ggpht.com/_juzpwZu8gCw/ShejSluPYAI/AAAAAAAAACs/V0ZA6M9CQhk/search-results_thumb%5B1%5D.jpg?imgmax=800" width="527" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;I want to test the behavior when a user clicks one of the “[show]” links in the Task Search results list.&amp;#160; The application should show the Task Detail view when this happens:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/ShejS0KQg5I/AAAAAAAAACw/s7WTbQhrFqk/s1600-h/task-detail%5B3%5D.jpg"&gt;&lt;img title="task-detail" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="292" alt="task-detail" src="http://lh5.ggpht.com/_juzpwZu8gCw/ShejTNDB58I/AAAAAAAAAC0/s8_HOQXC21s/task-detail_thumb%5B1%5D.jpg?imgmax=800" width="522" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;So I’ll name my test accordingly:&lt;/p&gt;  &lt;pre class="code"&gt;[&lt;span style="color: teal"&gt;Test&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;Should_call_show_task_when_task_selected_event_is_raised()&lt;br /&gt;{         &lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Before I start into the body of this test method, there is some setup code that I need to get in place:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;[&lt;span style="color: teal"&gt;TestFixture&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: teal"&gt;TaskManagerControllerTests&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: teal"&gt;MockRepository &lt;/span&gt;mocks;&lt;br /&gt;    &lt;span style="color: teal"&gt;ITaskManagerView &lt;/span&gt;viewMock;&lt;br /&gt;    &lt;span style="color: teal"&gt;ITaskRepository &lt;/span&gt;repository;&lt;br /&gt;&lt;br /&gt;    [&lt;span style="color: teal"&gt;SetUp&lt;/span&gt;]&lt;br /&gt;    &lt;span style="color: blue"&gt;public void &lt;/span&gt;SetUp()&lt;br /&gt;    {&lt;br /&gt;        mocks = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;MockRepository&lt;/span&gt;();&lt;br /&gt;        viewMock = mocks.DynamicMock&amp;lt;&lt;span style="color: teal"&gt;ITaskManagerView&lt;/span&gt;&amp;gt;();&lt;br /&gt;        repository = mocks.CreateMock&amp;lt;&lt;span style="color: teal"&gt;ITaskRepository&lt;/span&gt;&amp;gt;();&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The mock view will allow me to test my controller code without needing to have a view implementation (ASPX page) wired up and working first.&amp;#160; First I arrange the needed elements for the test by creating the event arguments object that will be passed with the event and an event raiser named “clickEvent”:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;[&lt;span style="color: teal"&gt;Test&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;Should_call_show_task_when_task_selected_event_is_raised()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;string &lt;/span&gt;taskID = &lt;span style="color: maroon"&gt;&amp;quot;12345678-1234-1234-1234-123456789012&amp;quot;&lt;/span&gt;;&lt;br /&gt;    &lt;span style="color: teal"&gt;TaskSelectedEventArgs &lt;/span&gt;e = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;TaskSelectedEventArgs&lt;/span&gt;(taskID);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: teal"&gt;IEventRaiser &lt;/span&gt;clickEvent = &lt;span style="color: teal"&gt;Expect&lt;br /&gt;        &lt;/span&gt;.Call(&lt;span style="color: blue"&gt;delegate &lt;/span&gt;{ viewMock.TaskSelected += &lt;span style="color: blue"&gt;null&lt;/span&gt;; })&lt;br /&gt;        .IgnoreArguments()&lt;br /&gt;        .GetEventRaiser();&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Next I use the Rhino mocks “Expect.Call” method to state that I expect the view’s “ShowTask” method to be called with with my test task ID as the parameter.&amp;#160; The expectations are finished with a call to the R.M. “ReplayAll” method:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;[&lt;span style="color: teal"&gt;Test&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;Should_call_show_task_when_task_selected_event_is_raised()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;string &lt;/span&gt;taskID = &lt;span style="color: maroon"&gt;&amp;quot;12345678-1234-1234-1234-123456789012&amp;quot;&lt;/span&gt;;&lt;br /&gt;    &lt;span style="color: teal"&gt;TaskSelectedEventArgs &lt;/span&gt;e = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;TaskSelectedEventArgs&lt;/span&gt;(taskID);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: teal"&gt;IEventRaiser &lt;/span&gt;clickEvent = &lt;span style="color: teal"&gt;Expect&lt;br /&gt;        &lt;/span&gt;.Call(&lt;span style="color: blue"&gt;delegate &lt;/span&gt;{ viewMock.TaskSelected += &lt;span style="color: blue"&gt;null&lt;/span&gt;; })&lt;br /&gt;        .IgnoreArguments()&lt;br /&gt;        .GetEventRaiser();&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: teal"&gt;Expect&lt;/span&gt;.Call(&lt;span style="color: blue"&gt;delegate &lt;/span&gt;{ viewMock.ShowTask(e.TaskID); })&lt;br /&gt;        .Repeat.Once();&lt;br /&gt;    mocks.ReplayAll();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Finally, I execute the code that will test this behavior… I instantiate a new TaskManagerController (this happens when the view is created) and raise the click event.&amp;#160; The R.M. “VerifyAll” method verifies that all my expectations are met when the code executed:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;[&lt;span style="color: teal"&gt;Test&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;Should_call_show_task_when_task_selected_event_is_raised()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;string &lt;/span&gt;taskID = &lt;span style="color: maroon"&gt;&amp;quot;12345678-1234-1234-1234-123456789012&amp;quot;&lt;/span&gt;;&lt;br /&gt;    &lt;span style="color: teal"&gt;TaskSelectedEventArgs &lt;/span&gt;e = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;TaskSelectedEventArgs&lt;/span&gt;(taskID);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: teal"&gt;IEventRaiser &lt;/span&gt;clickEvent = &lt;span style="color: teal"&gt;Expect&lt;br /&gt;        &lt;/span&gt;.Call(&lt;span style="color: blue"&gt;delegate &lt;/span&gt;{ viewMock.TaskSelected += &lt;span style="color: blue"&gt;null&lt;/span&gt;; })&lt;br /&gt;        .IgnoreArguments()&lt;br /&gt;        .GetEventRaiser();&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: teal"&gt;Expect&lt;/span&gt;.Call(&lt;span style="color: blue"&gt;delegate &lt;/span&gt;{ viewMock.ShowTask(e.TaskID); })&lt;br /&gt;        .Repeat.Once();&lt;br /&gt;    mocks.ReplayAll();&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;TaskManagerController&lt;/span&gt;(viewMock, repository);&lt;br /&gt;    clickEvent.Raise(&lt;span style="color: blue"&gt;null&lt;/span&gt;, e);&lt;br /&gt;    mocks.VerifyAll();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;That’s it.&amp;#160; The code to make it pass is painfully simple (painful because the code to test it is so much longer… I’m getting less and less offended by this state of affairs though, since the tests look like this for complicated code under test as well.&amp;#160; Besides, these tests copy and paste very easily!)&amp;#160; Here is the code from the controller under test:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;private void &lt;/span&gt;OnTaskSelected(&lt;span style="color: blue"&gt;object &lt;/span&gt;sender, &lt;span style="color: teal"&gt;TaskSelectedEventArgs &lt;/span&gt;e)&lt;br /&gt;{&lt;br /&gt;    view.ShowTask(e.TaskID);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;And that makes it pass:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;img title="tests-passed" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="106" alt="tests-passed" src="http://lh3.ggpht.com/_juzpwZu8gCw/ShejTVHStbI/AAAAAAAAAC4/_n9L3pp5w0U/tests-passed%5B3%5D.jpg?imgmax=800" width="407" border="0" /&gt; &lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&amp;#160;&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/1240921806131117404-38913099659954402?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/38913099659954402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/05/revving-up-tdd-engine.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/38913099659954402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/38913099659954402'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/05/revving-up-tdd-engine.html' title='Revving up the TDD engine'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_juzpwZu8gCw/ShejSHkpmYI/AAAAAAAAACk/B8P5YNwnfic/s72-c/tach_thumb%5B1%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-3424895806847966406</id><published>2009-05-14T01:08:00.001-07:00</published><updated>2009-05-14T01:08:36.430-07:00</updated><title type='text'>Do we have time to write tests on our projects?</title><content type='html'>&lt;p&gt;This is a question I would like to answer in the context of my day job.&amp;#160; It came up again today as I was talking with the other developers about some of the “new methods” I would like to integrate into our development process.&amp;#160; The title of this post is how the question sounds &lt;em&gt;now&lt;/em&gt; in my head.&amp;#160; This afternoon it went more like this: “We don’t have time to write tests on our projects!”&lt;/p&gt;  &lt;p&gt;Granted the developer who made this statement is a big believer in the “speak first, reason it out later” method of communicating… this is his knee-jerk reaction to someone telling him we would write &lt;em&gt;more&lt;/em&gt; code for the same functionality.&amp;#160; I can sympathize with him… we have deadlines on our projects to meet.&amp;#160; But I don’t agree with him.&amp;#160; I’ve already written some tests at work to solve a problem in the business logic of software we released 2 months ago.&amp;#160; The exercise helped me find other problems in that code and refactor it to a state that will be much easier to maintain.&amp;#160; Do we have time to maintain legacy code that is hard to understand and has no tests written against it?&amp;#160; I don’t.&lt;/p&gt;  &lt;p&gt;But… even though I am convinced we need to write tests, I want to be practical.&amp;#160; Will my team benefit from this?&amp;#160; Are there certain types of developers or certain types of projects that will never see the benefit of writing tests against code?&amp;#160; Maybe so – but I hope that isn’t a description of the team I work with.&amp;#160; And I think the best way to find out is to do the best I can to &lt;em&gt;set us up for success&lt;/em&gt;.&amp;#160; That is a big reason I am writing about my experience building up the NCharlie project.&amp;#160; I have some work to do before we are there though.&amp;#160; As I mentioned before, TDD still feels like a pain point for me because of the way my tests look using Rhino Mocks to mock objects like the database access code.&amp;#160; When I mentioned this pain point to my &lt;a title="Headspring Systems" href="http://www.headspringsystems.com/services/training/"&gt;boot-camp&lt;/a&gt; instructor his advice was to “abandon ship”… not on TDD, but on the Rhino Mocks Expect-Replay-Verify model and use the easier to understand Arrange-Act-Assert model of testing (I know AAA is possible with Rhino Mocks… but not with nice syntax in .Net 2.0).&lt;/p&gt;  &lt;p&gt;So, I am going to follow my instructor’s advice and abandon ship.&amp;#160; I’m going to start writing tests without a mocking framework.&amp;#160; I can create a class &lt;a title="Code generation using Resharper" href="http://search.twitter.com/search?phrase=resharperfavorites&amp;amp;from=charliesolomon"&gt;quickly&lt;/a&gt; that mocks a database access class &lt;em&gt;without&lt;/em&gt; using Rhino Mocks.&amp;#160; Sure it will be a bit more code to write, but I think the clarity it will bring to the tests will be worth it.&amp;#160; I also think it will be a good exercise even if I do end up coming back to a mocking framework like Rhino Mocks… I will appreciate it more after manually building mock objects.&lt;/p&gt;  &lt;p&gt;This means I am going to stay on the topic of writing tests for at least one more post.&amp;#160; &lt;a href="http://charliesolomon.blogspot.com/2009/04/ncharlie-test-driven-development-part-1.html"&gt;Earlier&lt;/a&gt; in this series I wrote some tests to verify that the controller class for the Task Search page in the NCharlie project was correctly responding to events raised in the view.&amp;#160; I didn’t show the code for the test in the post (it was available in the &lt;a title="NCharlie" href="http://code.google.com/p/ncharlie/"&gt;code repository&lt;/a&gt;) because I did not want to distract from the intent of that post.&amp;#160; Now it is time to show that code… almost (actually, in the next post).&amp;#160; First, let me answer the question I posed in the title of this post:&lt;/p&gt;  &lt;p&gt;Yes, we do have time.&amp;#160; It will make us go faster because our code will end up being easier to work in now, and easier to extend or fix later.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-3424895806847966406?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/3424895806847966406/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/05/do-we-have-time-to-write-tests-on-our.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/3424895806847966406'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/3424895806847966406'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/05/do-we-have-time-to-write-tests-on-our.html' title='Do we have time to write tests on our projects?'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-7018328164802513585</id><published>2009-05-05T23:29:00.001-07:00</published><updated>2009-05-13T23:54:30.248-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Supervising Controller'/><category scheme='http://www.blogger.com/atom/ns#' term='NCharlie'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>TDD Pain Points, A Retrospective</title><content type='html'>&lt;p&gt;This is the fourth weekend into writing about test driven development… taking agile methods I learned in &lt;a title="Headspring Systems" href="http://www.headspringsystems.com/services/training/"&gt;boot-camp&lt;/a&gt; back to work.&amp;#160; Time for a retrospective…&lt;/p&gt;  &lt;p&gt;First I will say that writing about this process has been a tremendous help in making these concepts gel.&amp;#160; Try teaching someone about something you are trying to learn.&amp;#160; I don’t care if no one ever reads these posts – the process of writing them has been invaluable to me.&lt;/p&gt;  &lt;p&gt;Now, for the pain points:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Supervising controller:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;I chose this pattern (a flavor of Model-View-Presenter) for two reasons.&amp;#160; 1) We use .Net 2.0 at work and our developers have a lot invested in webforms.&amp;#160; 2) It helps me separate functionality away from code-behind files and adhere to the &lt;a title="SOLID principles of Object Oriented Programming" href="http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod"&gt;Single Responsibility Principle&lt;/a&gt;, which is especially important if the code will be tested. &lt;/p&gt;  &lt;p&gt;I like &lt;em&gt;most&lt;/em&gt; of the Supervising Controller pattern in the context of webforms.&amp;#160; What I don’t like (pain point) is the &amp;quot;loop&amp;quot; of event handling: view fires event, then raises another event that the controller is subscribed to, then the controller responds by executing some logic and then calling a function back in the view to render results.&amp;#160; This is convoluted... I like the separation of concerns, but I don't like having to subscribe to events more than once.&amp;#160; Can I just call controller methods directly from the view?&amp;#160; Maybe what I'm describing is called the &amp;quot;Passive Controller&amp;quot;... I need to look into it.&amp;#160; This is really, probably, a pain point that should be attributed to webform development.&amp;#160; But hey, webforms pay the bills!&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;TDD:&lt;/strong&gt; &lt;/p&gt;  &lt;p&gt;Yes, as of now I’m listing Test Driven Development as a pain point.&amp;#160; I really like testing my code.&amp;#160; I don't really like the syntax I'm using to mock functionality.&amp;#160; Mocking my dependencies is necessary to write unit tests, but the Record, Replay, Verify syntax in Rhino Mocks is not natural for me to set up and harder to explain to my team.&amp;#160; I am still trying to shove the Rhino Mocks 3.3 RRV syntax into the Arrange, Act, Assert (AAA) method I learned in boot-camp.&amp;#160; I need to change the way I'm using Rhino Mocks, or I need to start using a different mocking framework (Rhino Mocks supports the AAA syntax and is &lt;a title="Rhino Mocks 3.5 Arrange, Act, Assert" href="http://ayende.com/Wiki/Rhino+Mocks+3.5.ashx#Arrange,Act,Assert"&gt;explained really well&lt;/a&gt;... Using .net 3.x lamba expressions.&amp;#160; AAA is possible with Rhino Mocks, but the &lt;a title="Rhino Mocks AAA syntax for .Net 2.0" href="http://ayende.com/Wiki/Rhino+Mocks+3.5.ashx#UsingtheAAAsyntaxinCCA"&gt;2.0 syntax is painful&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;That’s it for pain points for now.&amp;#160; These by no means mean I am giving up on TDD or Supervising Controller.&amp;#160; Although painful right now, I think these are birth-pangs… I am hopeful that in 2 or 3 months they will be big giant assets in my toolbox.&amp;#160; I’ll end now on a (what is the opposite of a pain point?) &lt;em&gt;happy&lt;/em&gt; point: Resharper 4.5 is incredible.&amp;#160; It makes me go faster… I intend to dedicate a future post to my top X favorite features.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://charliesolomon.blogspot.com/2009/04/ncharlie-tdd-part-3-ui-test.html"&gt;&amp;lt;&amp;lt; NCharlie: TDD – Part 3 (a UI test)&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/1240921806131117404-7018328164802513585?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/7018328164802513585/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/05/tdd-pain-points-retrospective.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/7018328164802513585'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/7018328164802513585'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/05/tdd-pain-points-retrospective.html' title='TDD Pain Points, A Retrospective'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-2878148764673242040</id><published>2009-05-05T22:57:00.001-07:00</published><updated>2009-05-05T22:57:03.262-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geo Tracks'/><title type='text'>Geo Tracks – 2009 May Cinco</title><content type='html'>&lt;p&gt;I’m a little late this week because we moved last weekend.&amp;#160; Now I am closer to work (cut 2 hours per day off the commute!)… which means less time with the podcasts and more time with the family – a nice trade I think.&amp;#160; I’m still listening to book one in the &lt;a title="iTunes Store link" href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewAudiobook?id=270311778&amp;amp;s=143441"&gt;Wheel of Time&lt;/a&gt; series, and will be for while.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-2878148764673242040?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/2878148764673242040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/05/geo-tracks-2009-may-cinco.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2878148764673242040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2878148764673242040'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/05/geo-tracks-2009-may-cinco.html' title='Geo Tracks – 2009 May Cinco'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-2436176462864158407</id><published>2009-04-25T12:02:00.001-07:00</published><updated>2009-04-25T21:30:07.193-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geo Tracks'/><title type='text'>Geo Tracks – 2009 Apr 25</title><content type='html'>&lt;p&gt;&lt;a title="Hanselminutes 158" href="http://www.hanselminutes.com/default.aspx?showID=176"&gt;Hanselminutes 158&lt;/a&gt; - Visiting Fog Creek Software and Joel Spolsky… I’m still not sure what exactly FogBugz is, but I learned a lot about how it was built and has evolved? Also, good to get some encouragement to keep writing, even if no one ever notices your blog :)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.listenersbible.com/products/index.php?main_page=product_custom_info&amp;amp;cPath=7_24_26&amp;amp;products_id=21"&gt;Psalm 80 – Psalm 85&lt;/a&gt; ESV read by Max McLean&lt;/p&gt;  &lt;p&gt;I started book 1, The Eye of the World, in Robert Jordan’s Wheel of Time series. Jeremy Miller put this at the top of &lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2009/04/14/my-top-10-list-of-scifi-fantasy-series-way-off-topic.aspx"&gt;his book list&lt;/a&gt;… I had never heard of it, so I’m giving it a try.&amp;#160; (Thanks for the iTunes gift cert Lucas!)&lt;/p&gt;  &lt;p&gt;That’s it for this week… I worked from home Friday, so no commute.&amp;#160; Thursday I brought my wife to work with me so she could spend the day looking at places to live and schools in the area I work in… which means no podcasts since the wife likes to talk to me instead. :)&amp;#160; We may be moving closer to work soon!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-2436176462864158407?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/2436176462864158407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/geo-tracks-2009-apr-25.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2436176462864158407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2436176462864158407'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/geo-tracks-2009-apr-25.html' title='Geo Tracks – 2009 Apr 25'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-7135830582887244445</id><published>2009-04-25T11:58:00.001-07:00</published><updated>2009-05-13T23:53:12.125-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NCharlie'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>NCharlie: TDD – part 3 (a UI test)</title><content type='html'>&lt;p&gt;In this post I tackle the last task on my list for the task manager search feature: “Write tests, then implement the ShowSearchResults method.”&lt;/p&gt;  &lt;p&gt;In &lt;a title="NCharlie TDD part 1" href="http://charliesolomon.blogspot.com/2009/04/ncharlie-test-driven-development-part-1.html"&gt;part one&lt;/a&gt; I wrote unit tests to verify my TaskManagerController class is listening (subscribed) to the SearchRequested event, and that it correctly responds by calling the TaskManagerView class “ShowSearchResults” method.&amp;#160; In &lt;a title="NCharlie TDD part 2" href="http://charliesolomon.blogspot.com/2009/04/ncharlie-tdd-part-2-integration-test.html"&gt;part two&lt;/a&gt; I verified that the TaskRepository correctly returns tasks when the Search method is called.&lt;/p&gt;  &lt;p&gt;I admit: as I write this sentence I’m not sure how I will accomplish writing a test to verify that ShowSearchResults works… so I posted the question to Twitter:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;&lt;strong&gt;I wrote a unit test to verify my controller calls my view's &amp;quot;ShowResult&amp;quot; method... What kind of test verifies ShowResult shows the result?&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;…and got a reply from my boot-camp instructor:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;&lt;strong&gt;@&lt;/strong&gt;&lt;/em&gt;&lt;a href="http://twitter.com/charliesolomon"&gt;&lt;em&gt;&lt;strong&gt;charliesolomon&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;&lt;em&gt;&lt;strong&gt; we automate testing the ui. don't know that there's a great name for that other than automated ui test. or just ui test.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;&lt;strong&gt;@&lt;/strong&gt;&lt;/em&gt;&lt;a href="http://twitter.com/mhinze"&gt;&lt;em&gt;&lt;strong&gt;mhinze&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;&lt;em&gt;&lt;strong&gt; So at this point (UI testing) it is time for me to use WatiN or equivalent?&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;&lt;strong&gt;@&lt;/strong&gt;&lt;/em&gt;&lt;a href="http://twitter.com/charliesolomon"&gt;&lt;em&gt;&lt;strong&gt;charliesolomon&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;&lt;em&gt;&lt;strong&gt; yes, sounds like it.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;So here I go… &lt;a title="WatiN" href="http://watin.sourceforge.net/"&gt;WatiN&lt;/a&gt; is an open source web application test framework for .Net that will spin up an instance of my web browser and verify things on a web page.&amp;#160; To verify that ShowSearchResults is working I want to verify that the correct search results display on screen when the ShowSearchResults method is called on the TaskManagerView.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_juzpwZu8gCw/SfNdT_RqGUI/AAAAAAAAACA/QWFXzjSrYtg/s1600-h/TaskManagerSearch_23.png"&gt;&lt;img title="TaskManagerSearch_2" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="222" alt="TaskManagerSearch_2" src="http://lh5.ggpht.com/_juzpwZu8gCw/SfNdUE5BslI/AAAAAAAAACE/B-FZdrANFtU/TaskManagerSearch_2_thumb1.png?imgmax=800" width="383" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;First I create a new class library project named UITests.&amp;#160; I want to keep these tests in a separate project because they take much longer to run than my unit tests.&amp;#160; If I’m going to keep this example strictly “Test First”, I need to, well, write my test first:&lt;/p&gt;  &lt;pre class="code"&gt;[&lt;span style="color: teal"&gt;Test&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;Should_show_search_results()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: teal"&gt;Task &lt;/span&gt;task = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;);&lt;br /&gt;    &lt;span style="color: teal"&gt;TaskRepository &lt;/span&gt;repository = &lt;br /&gt;        &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;TaskRepository&lt;/span&gt;(&lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;HybridSessionBuilder&lt;/span&gt;());&lt;br /&gt;    repository.Save(task);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: teal"&gt;IE &lt;/span&gt;ie = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;IE&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;http://localhost:1035/Default.aspx&amp;quot;&lt;/span&gt;))&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: teal"&gt;Button &lt;/span&gt;search = ie.Button(&lt;span style="color: teal"&gt;Find&lt;/span&gt;.ById(&lt;span style="color: maroon"&gt;&amp;quot;btnSearch&amp;quot;&lt;/span&gt;));&lt;br /&gt;        search.Click();&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: teal"&gt;Div &lt;/span&gt;tasks = ie.Div(&lt;span style="color: teal"&gt;Find&lt;/span&gt;.ById(&lt;span style="color: maroon"&gt;&amp;quot;tasks&amp;quot;&lt;/span&gt;));&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: teal"&gt;Assert&lt;/span&gt;.IsTrue(tasks.InnerHtml.IndexOf(&lt;span style="color: maroon"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;) &amp;gt;= 0);                &lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I’m not showing the [SetUp] method here for this Test Fixture… it ensures I have a clean database before running this test.&amp;#160; The test above just adds a task to the database, fires up the web page and verifies the search results come up correctly.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Default.aspx is a very simple page… just a text field, button and repeater to show the results. The “ShowSearchResults” method is what I’m interested in implementing and testing for this feature.&amp;#160; For the sake of brevity in this post, I will just refer you to the &lt;a href="http://code.google.com/p/ncharlie/source/browse/#svn/branches/tdd-demo"&gt;code&lt;/a&gt; to see how the ASPX page and code-behind file are constructed.&amp;#160; Here is the ShowSearchResults method from Default.aspx.cs:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;ShowSearchResults(&lt;span style="color: teal"&gt;ICollection&lt;/span&gt;&amp;lt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;&amp;gt; tasks)&lt;br /&gt;{&lt;br /&gt;    rptTasks.DataSource = tasks;&lt;br /&gt;    rptTasks.DataBind();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I run my test and see WatiN fire up Internet Explorer, click the “Get” button, then search the results for my test record…&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_juzpwZu8gCw/SfNdUICtRSI/AAAAAAAAACI/Gc_CAm2dJCg/s1600-h/watin-clicking-get%5B3%5D.jpg"&gt;&lt;img title="watin-clicking-get" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="152" alt="watin-clicking-get" src="http://lh3.ggpht.com/_juzpwZu8gCw/SfNdUeBuejI/AAAAAAAAACM/HNDPTpGS_Sg/watin-clicking-get_thumb%5B1%5D.jpg?imgmax=800" width="314" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_juzpwZu8gCw/SfNdUpJM0nI/AAAAAAAAACQ/HJ3Ici3T59Y/s1600-h/watin-showing-results%5B3%5D.jpg"&gt;&lt;img title="watin-showing-results" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="192" alt="watin-showing-results" src="http://lh3.ggpht.com/_juzpwZu8gCw/SfNdU_swOrI/AAAAAAAAACU/4kJtR7ouejo/watin-showing-results_thumb%5B1%5D.jpg?imgmax=800" width="349" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;…and the Resharper test runner shows the results:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_juzpwZu8gCw/SfNdVIGRVsI/AAAAAAAAACY/RO7PvCkJdMY/s1600-h/ui-tests-passed%5B3%5D.jpg"&gt;&lt;img title="ui-tests-passed" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="117" alt="ui-tests-passed" src="http://lh5.ggpht.com/_juzpwZu8gCw/SfNdVVMkpRI/AAAAAAAAACc/N-y6JpTn3CY/ui-tests-passed_thumb%5B1%5D.jpg?imgmax=800" width="433" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;That’s it!&amp;#160; Of course it looks really simple here, and it is… but it actually took me 3 hours to get this simple test working.&amp;#160; Here’s why:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;NUnit uses multi-threading by default, WatiN requires a single-threaded execution.&amp;#160; I solved this using a config file (“NCharlie.UITests.dll.config”… &lt;em&gt;naming&lt;/em&gt; this config file correctly was actually the biggest time sink for this exercise.&amp;#160; I ended up adding a test to check that my config file was being read – &lt;a title="How NUnit Finds Config Files" href="http://nunit.com/blogs/?p=9"&gt;thanks Charlie Poole&lt;/a&gt;)&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;For more information, check out the &lt;a href="http://code.google.com/p/ncharlie/source/browse/#svn/branches/tdd-demo"&gt;tdd-demo code branch&lt;/a&gt; and see the &lt;a title="WatiN getting started guide" href="http://watin.sourceforge.net/gettingstarted.html"&gt;WatiN getting started guide&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a title="NCharlie: TDD – part 2 (an integration test)" href="http://charliesolomon.blogspot.com/2009/04/ncharlie-tdd-part-2-integration-test.html"&gt;&amp;lt;&amp;lt; NCharlie: TDD – part 2 (an integration test)&lt;/a&gt;&amp;#160; &lt;a href="http://charliesolomon.blogspot.com/2009/05/tdd-pain-points-retrospective.html"&gt;TDD Pain Points, A Retrospective &amp;gt;&amp;gt;&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/1240921806131117404-7135830582887244445?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/7135830582887244445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/ncharlie-tdd-part-3-ui-test.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/7135830582887244445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/7135830582887244445'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/ncharlie-tdd-part-3-ui-test.html' title='NCharlie: TDD – part 3 (a UI test)'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_juzpwZu8gCw/SfNdUE5BslI/AAAAAAAAACE/B-FZdrANFtU/s72-c/TaskManagerSearch_2_thumb1.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-2993264552703361958</id><published>2009-04-18T14:16:00.001-07:00</published><updated>2009-04-18T14:16:31.665-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NCharlie'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>NCharlie: TDD – part 2 (an integration test)</title><content type='html'>&lt;p&gt;In my &lt;a title="Previous post in this series" href="http://charliesolomon.blogspot.com/2009/04/ncharlie-test-driven-development-part-1.html"&gt;last post of this series&lt;/a&gt; I wrote unit tests to verify that an event (SearchRequested) from the View was handled by the Controller in my Agile/TDD sandbox application &lt;a href="http://code.google.com/p/ncharlie"&gt;NCharlie&lt;/a&gt;.&amp;#160; I started with a sequence diagram showing the interactions needed to accomplish a search, then wrote down the tasks needed to complete this feature:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Create interfaces for TaskManagerView and TaskRepository. &lt;/li&gt;    &lt;li&gt;Write tests, then write code to raise and handle the SearchRequestEvent method. &lt;/li&gt;    &lt;li&gt;Write tests, then implement the Search method. &lt;/li&gt;    &lt;li&gt;Write tests, then implement the ShowSearchResults method. &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Two down, two to go.&amp;#160; The first two tasks could be test driven using unit tests.&amp;#160; Rhino Mocks allowed me to create a mock View and a mock Repository and concentrate on the TaskManagerController class… making sure it responded and handled the SearchRequested event correctly.&amp;#160; Now I want to wire up the Search method to return some actual results, and I want to show those results in an ASPX page, like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/SepDLKbIi1I/AAAAAAAAABw/8ChMgL-At7A/s1600-h/TaskManagerSearch3.png"&gt;&lt;img title="TaskManagerSearch" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="222" alt="TaskManagerSearch" src="http://lh6.ggpht.com/_juzpwZu8gCw/SepDLXDyfsI/AAAAAAAAAB0/a6pRp6jUwvY/TaskManagerSearch_thumb1.png?imgmax=800" width="383" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Since the Search method will be interacting with my database, I’ll need to test it using an &lt;em&gt;integration test&lt;/em&gt;.&amp;#160; I like the way CodeCampServer keeps integration tests in a separate project… from the Resharper test runner it is easy to run all the tests in a project – so keeping the fast-executing unit tests separated seems to be a good idea.&amp;#160; I will add a new project to the NCharlie solution for integration tests and get to work (source is available in the &lt;a title="NCharlie TDD Demo source" href="http://code.google.com/p/ncharlie/source/browse/#svn/branches/tdd-demo"&gt;tdd-demo branch&lt;/a&gt; of the NCharlie project.)&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Here is the test method I want to build:&lt;/p&gt;  &lt;pre class="code"&gt;[&lt;span style="color: teal"&gt;Test&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;Should_search_by_task_description()...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To write this test I will need to able to save Task entities to the database, retrieve them using the Search method, and make sure that the returned items contain the correct search results.&amp;#160; The saving part is accomplished in NCharlie using NHibernate and some helper classes that can be found in the open source projects &lt;a title="CodeCampServer" href="http://code.google.com/p/codecampserver/"&gt;CodeCampServer&lt;/a&gt; and &lt;a title="Tarantino on Google Code" href="http://code.google.com/p/tarantino/"&gt;Tarantino&lt;/a&gt;: IRepository, PersistentObject and HybridSessionBuilder.&amp;#160; These are mostly the same in NCharlie, though I did make a few changes so they would be .Net 2.0-friendly for this project.&amp;#160; The “PersistEntities” method below saves entities to the database, ensures the transaction is committed and the session is closed, so that subsequent calls to the database in this test can count on the entities being there.&amp;#160; I have not shown it here, but when running integration tests against a database it is important to setup the test first and make sure the database is in a consistent state.&amp;#160; This is done via a [SETUP] method in the RepositoryTestBase class, which my TaskRepositoryTester class is derived from.&amp;#160; Here is the test that verifies the search method is working correctly:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;[&lt;span style="color: teal"&gt;Test&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;Should_search_by_task_description()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: teal"&gt;Task &lt;/span&gt;task1 = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;);&lt;br /&gt;    &lt;span style="color: teal"&gt;Task &lt;/span&gt;task2 = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;xFOo1&amp;quot;&lt;/span&gt;);&lt;br /&gt;    &lt;span style="color: teal"&gt;Task &lt;/span&gt;task3 = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;no fu&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;    PersistEntities(task1, task2, task3);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: teal"&gt;TaskSearchSpecification &lt;/span&gt;searchSpecification = &lt;br /&gt;        &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;TaskSearchSpecification&lt;/span&gt;();&lt;br /&gt;    searchSpecification.Description = &lt;span style="color: maroon"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: teal"&gt;ITaskRepository &lt;/span&gt;repository = &lt;br /&gt;        &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;TaskRepository&lt;/span&gt;(&lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;HybridSessionBuilder&lt;/span&gt;());&lt;br /&gt;    &lt;span style="color: teal"&gt;Task&lt;/span&gt;[] tasks = repository.Search(searchSpecification);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: teal"&gt;CollectionAssert&lt;/span&gt;.Contains(tasks, task1);&lt;br /&gt;    &lt;span style="color: teal"&gt;CollectionAssert&lt;/span&gt;.Contains(tasks, task2);&lt;br /&gt;    &lt;span style="color: teal"&gt;CollectionAssert&lt;/span&gt;.DoesNotContain(tasks, task3);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The “TaskSearchSpecification” class contains the search input fields (in this example I’m only searching on the Description field.)&amp;#160; Finally, I call the actual “Search” method on the TaskRepository and verify the results using the “CollectionAssert” class (part of NUnit.)&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This test fails initially, and I implement the search method in TaskRepository to make it pass:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;[] Search(&lt;span style="color: teal"&gt;TaskSearchSpecification &lt;/span&gt;specification)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: teal"&gt;ICriteria &lt;/span&gt;criteria = GetSession()&lt;br /&gt;        .CreateCriteria(&lt;span style="color: blue"&gt;typeof &lt;/span&gt;(&lt;span style="color: teal"&gt;Task&lt;/span&gt;));&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: teal"&gt;IList&lt;/span&gt;&amp;lt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;&amp;gt; tasks = criteria&lt;br /&gt;        .Add(&lt;span style="color: teal"&gt;Restrictions&lt;br /&gt;            &lt;/span&gt;.InsensitiveLike(&lt;span style="color: maroon"&gt;&amp;quot;Description&amp;quot;&lt;/span&gt;, &lt;br /&gt;                specification.Description, &lt;br /&gt;                &lt;span style="color: teal"&gt;MatchMode&lt;/span&gt;.Anywhere)&lt;br /&gt;            )&lt;br /&gt;        .List&amp;lt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;&amp;gt;();&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: teal"&gt;Task&lt;/span&gt;[] taskArray = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;[tasks.Count];&lt;br /&gt;    tasks.CopyTo(taskArray, 0);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;return &lt;/span&gt;taskArray;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_juzpwZu8gCw/SepDLhbVzdI/AAAAAAAAAB4/0prj60pGaOw/s1600-h/task-search-test%5B3%5D.jpg"&gt;&lt;img title="task-search-test" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="185" alt="task-search-test" src="http://lh5.ggpht.com/_juzpwZu8gCw/SepDLwv_JlI/AAAAAAAAAB8/MBGn-DjecTs/task-search-test_thumb%5B1%5D.jpg?imgmax=800" width="544" border="0" /&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;For more information:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;As I was writing this post I ran across Billy McCafferty’s article on &lt;a title="NHibernate Best Practices with ASP.Net" href="http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx"&gt;NHibernate Best Practices with ASP.Net&lt;/a&gt;… I highly recommend it if you are interested in how these patterns are implemented.&amp;#160; I know things have changed in our field since he wrote this article (Billy later contributed the &lt;a title="S#arp architechture" href="http://devlicio.us/blogs/billy_mccafferty/archive/2008/04/21/asp-net-mvc-best-practices-with-nhibernate-and-spring-net.aspx"&gt;S#arp architecture&lt;/a&gt; which uses ASP.Net MVC), but this article is a great resource… especially for someone like me who works in a place where Webforms are still the tool we use to get our work done.&amp;#160; It is also very apparent from reading Billy’s article just how much it has influenced the ASP.Net world since it was written.&amp;#160; The CodeCampServer project I’m basing NCharlie on uses much of the architecture and patterns first described in Billy’s article and later in his S#harp architecture.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://charliesolomon.blogspot.com/2009/04/ncharlie-test-driven-development-part-1.html"&gt;&amp;lt;&amp;lt; Previous NCharlie: TDD – part1&lt;/a&gt;, Next NCharlie: TDD – part 3 &amp;gt;&amp;gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-2993264552703361958?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/2993264552703361958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/ncharlie-tdd-part-2-integration-test.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2993264552703361958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/2993264552703361958'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/ncharlie-tdd-part-2-integration-test.html' title='NCharlie: TDD – part 2 (an integration test)'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_juzpwZu8gCw/SepDLXDyfsI/AAAAAAAAAB0/a6pRp6jUwvY/s72-c/TaskManagerSearch_thumb1.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-6778585618013364643</id><published>2009-04-18T00:00:00.000-07:00</published><updated>2009-04-18T11:02:53.691-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geo Tracks'/><title type='text'>Geo Tracks – 2009 Apr 18</title><content type='html'>&lt;p&gt;&lt;a title="Alt.Net Podcast #18" href="http://www.altnetpodcast.com/episodes/18-talking-with-jeremy-miller-about-alt-net"&gt;Alt.Net Podcast #18&lt;/a&gt;, talking with Jeremy Miller about Alt.Net.&amp;#160; I like what Jeremy and James Avery have to say here.&amp;#160; My summary: Alties need to focus on improving their own craft instead of trying to force feed their current stack of tools/patterns/archs into the .Net mainstream.&lt;/p&gt;  &lt;p&gt;&lt;a title="Stack Overflow Podcast #47" href="http://blog.stackoverflow.com/2009/03/podcast-47/"&gt;Stack Overflow Podcast #47&lt;/a&gt; – Joel and Jeff talk about sketching mockups with Balsamiq Mockups (a favorite of mine), say they will talk about Eclipse but really don’t, and keep ramblin’ on and on…&lt;/p&gt;  &lt;p&gt;&lt;a title="Hanselminutae 5 with Richard Campbell" href="http://www.hanselminutes.com/default.aspx?showID=175"&gt;Hanselminutae 5 with Richard Campbell&lt;/a&gt; – Scott admits he is out to waste my time with this episode, but hey, I’ve got an hour and 15 to waste driving to work anyway!&lt;/p&gt;  &lt;p&gt;&lt;a title="SE Radio #129, F# with Luke Hoban" href="http://www.se-radio.net/podcast/2009-03/episode-129-f-luke-hoban"&gt;SE Radio #129, F# with Luke Hoban&lt;/a&gt; – Good talk about use cases for F#.&amp;#160; Luke thinks the increase of parallelism in software as we keep adding cores to CPUs will make languages like F# much more needed.&amp;#160; Also interesting that F# targets the ad-hoc algorithm crowd… people who analyze data using tools like MathCAD or Mathematica.&lt;/p&gt;  &lt;p&gt;&lt;a title="Why People Believe What They Do" href="http://rss.sciam.com/sciam/science-talk"&gt;Why People Believe What They Do&lt;/a&gt;, SciAm Podcast April 10, 2009 – Interesting discussion about a study on the correlation (or lack of) between education, religious affiliations, and beliefs in evolutionary theory and origins of life.&amp;#160; One general consensus was that humans naturally gravitate to solutions that show some sense of purpose vs. “it just happened randomly”.&lt;/p&gt;  &lt;p&gt;&lt;a title="Gospel Conference Part 5" href="http://storage.cornerstonesimi.com/sermons/audio/09_114_Gospel_Conference_pt5_Audio_Podcast.mp3"&gt;Gospel Conference Part 5&lt;/a&gt;, Francis Chan – Francis starts with Ephesians 2 and the hope of heaven… also talks through some of the ways his church (Cornerstone Simi) cares for and rescues orphans, poor and abused people around the world.&lt;/p&gt;  &lt;p&gt;&lt;a title="Psalm 37 – Psalm 50" href="http://www.listenersbible.com/products/index.php?main_page=product_custom_info&amp;amp;cPath=7_24_26&amp;amp;products_id=21"&gt;Psalm 37 – Psalm 50&lt;/a&gt; ESV read by Max McLean&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-6778585618013364643?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/6778585618013364643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/geo-tracks-2009-apr-18.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/6778585618013364643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/6778585618013364643'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/geo-tracks-2009-apr-18.html' title='Geo Tracks – 2009 Apr 18'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-1604478008243469723</id><published>2009-04-11T21:23:00.001-07:00</published><updated>2009-04-11T21:23:05.178-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Geo Tracks'/><title type='text'>Geo Tracks – 2009 Apr 11</title><content type='html'>&lt;p&gt;I spend a lot of time in the car each week.&amp;#160; This car, specifically (91 Geo Metro… 47 mpg!):&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_juzpwZu8gCw/SeFsp3z3xyI/AAAAAAAAABo/Y6sScY3nSPs/s1600-h/geo%5B3%5D.jpg"&gt;&lt;img title="geo" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="156" alt="geo" src="http://lh3.ggpht.com/_juzpwZu8gCw/SeFsqIMFQ-I/AAAAAAAAABs/fx3RXVPpp8I/geo_thumb%5B1%5D.jpg?imgmax=800" width="446" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I’m going to start linking to what I’m listening to during the commute each week (if I think they are worth sharing).&amp;#160; I’ll tag them “Geo Tracks”:&lt;/p&gt;  &lt;p&gt;&lt;a title=".NET Rocks #433" href="http://www.dotnetrocks.com/default.aspx?showNum=433" target="_blank"&gt;.NET Rocks #433: Phil Haack on ASP.NET MVC RTM!!&lt;/a&gt; Good explanation of MVC, and Supervising Controller / Passive Controller (MVP) models.&lt;/p&gt;  &lt;p&gt;&lt;a title=".NET Rocks #421" href="http://www.dotnetrocks.com/default.aspx?showNum=421" target="_blank"&gt;.NET Rocks #421: Derik Whittaker on nHibernate&lt;/a&gt; Good talk about use cases for nHibernate… but the best part of this show was Richard Campbell’s call out to the IT community (52:40) that the default monitor configuration for a developer should be 4960 by 1600 (30” in the middle, bookended by two 20” screens in portrait mode).&amp;#160; Yes please.&lt;/p&gt;  &lt;p&gt;&lt;a title="Stack Overflow Podcast #43" href="http://blog.stackoverflow.com/2009/02/podcast-43/" target="_blank"&gt;Stack Overflow Podcast #43&lt;/a&gt; Joel and Jeff discuss how to handle incompetent programmers, among other things.&amp;#160; Specific other things include Joel whining about the service he receives everywhere he travels… It is getting more difficult to finish these each time I listen to this podcast, but I don’t think I’ll give up yet.&amp;#160; I was really impressed with the Podcast site.&amp;#160; First time I’ve visited, since I always update through iTunes.&amp;#160; They really keep good show notes and are liberal with the links to resources.&lt;/p&gt;  &lt;p&gt;&lt;a title="Francis Chan - Living with Joy" href="http://storage.cornerstonesimi.com/sermons/audio/09_112_Living_With_Joy_Audio_Podcast.mp3"&gt;Francis Chan – 3/22/09 Living a Life that Matters Series: “Living with Joy”&lt;/a&gt; If you have never heard Francis speak, get ready to be convicted and changed.&amp;#160; Rejoice in the Lord… it is a command!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://rss.sciam.com/sciam/science-talk"&gt;Scientific American Podcast&lt;/a&gt; – &lt;a href="http://rss.sciam.com/click.phdo?i=a71f17f354bd372e5db8948d4d5c6c69"&gt;In Search of Time&lt;/a&gt; Journalist and writer Dan Falk talks about his new book In Search of Time, about the cultural, physical and psychological aspects of the mysterious ticking clocks all around us.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-1604478008243469723?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/1604478008243469723/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/geo-tracks-2009-apr-11.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/1604478008243469723'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/1604478008243469723'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/geo-tracks-2009-apr-11.html' title='Geo Tracks – 2009 Apr 11'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_juzpwZu8gCw/SeFsqIMFQ-I/AAAAAAAAABs/fx3RXVPpp8I/s72-c/geo_thumb%5B1%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-3564327889465397499</id><published>2009-04-11T15:13:00.001-07:00</published><updated>2009-04-11T17:33:44.714-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NCharlie'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>NCharlie: Test Driven Development – part 1</title><content type='html'>&lt;p&gt;This is the second post in a series that tracks my progress implementing some of the technologies, patterns and methods I picked up at an Agile .Net &lt;a title="Headspring System Training" href="http://www.headspringsystems.com/services/agile-training/"&gt;boot-camp&lt;/a&gt; in March 2009.&lt;/p&gt;  &lt;p&gt;The exercise for this post is to strip down my sandbox project, &lt;a title="NCharlie on Google Code" href="http://code.google.com/p/ncharlie" target="_blank"&gt;NCharlie&lt;/a&gt;, to a point where I can really see the benefit of test-first development.&amp;#160; Then I will add a feature to the project using the process my instructor followed (“User Story Execution Process”, described &lt;a title="User Story Execution Process - Matt Hinze" href="http://mhinze.com/user-story-execution-process/" target="_blank"&gt;here&lt;/a&gt;).&amp;#160; You can download the source for this exercise by browsing the &lt;a title="NCharlie TDD Demo" href="http://code.google.com/p/ncharlie/source/browse/#svn/branches/tdd-demo" target="_blank"&gt;tdd-demo branch&lt;/a&gt; of the NCharlie project on Google Code.&lt;/p&gt;  &lt;p&gt;The project is a simple task management tool.&amp;#160; The first feature I’ll implement is the search feature.&amp;#160; Here’s the requirement:&lt;/p&gt;  &lt;p&gt;“A user should be able to search for existing tasks.”&lt;/p&gt;  &lt;p&gt;I know, that is a pretty vague requirement.&amp;#160; I’ll add on more layers later… for now, this requirement will serve nicely (I hope) in demonstrating the TDD method.&amp;#160; I spent some time setting up my “Core” project which contains the domain model (for now, just one entity… a Task).&amp;#160; Next I create a sequence diagram describing a search for existing tasks:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/SeEWFymCMaI/AAAAAAAAABM/0twxHWRsPJY/s1600-h/taskmanagersearchsequence8.jpg"&gt;&lt;img title="task-manager-search-sequence" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="162" alt="task-manager-search-sequence" src="http://lh4.ggpht.com/_juzpwZu8gCw/SeEWGFf-hsI/AAAAAAAAABU/m3pD7mkOLfE/taskmanagersearchsequence_thumb4.jpg?imgmax=800" width="449" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Aside:&lt;/strong&gt; If you are staring at this diagram wondering why I’m using Views, Controllers and Repositories, it is because of the architecture decisions I’ve made for this project.&amp;#160; I hinted at the motivation for this architecture in the first post of this series… I may come back to that in more detail later.&amp;#160; For now though I will just admit that it was pretty easy to think about the ingredients for this sequence diagram because I had already built this and now I’m just starting over.&amp;#160; When I built NCharlie initially there was some pain I had to work through getting the parts of the &lt;a title="Phil Haack Supervising Controller" href="http://haacked.com/archive/2006/08/09/ASP.NETSupervisingControllerModelViewPresenterFromSchematicToUnitTestsToCode.aspx" target="_blank"&gt;Supervising Controller&lt;/a&gt; pattern to work in the context of a webforms application using the &lt;a title="Jeffrey Palermo - Onion Architecture" href="http://jeffreypalermo.com/blog/the-onion-architecture-part-1/" target="_blank"&gt;Onion Architecture&lt;/a&gt;.&amp;#160; But that is figured out now, and I won’t have to figure it out again on every application.&amp;#160; I know now that I need to model View events and handle those events in a Controller.&amp;#160; The Repository pattern is used to abstract away the persistence (database) part of the application. &lt;strong&gt;(end of Aside)&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Back to the exercise… I’ll break the elements of the sequence diagram down into development tasks:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Create interfaces for TaskManagerView and TaskRepository. &lt;/li&gt;    &lt;li&gt;Write tests, then write code to raise and handle the SearchRequestEvent method. &lt;/li&gt;    &lt;li&gt;Write tests, then implement the Search method. &lt;/li&gt;    &lt;li&gt;Write tests, then implement the ShowSearchResults method. &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Step 1: Here are the two interfaces:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: teal"&gt;ITaskManagerView &lt;/span&gt;: &lt;span style="color: teal"&gt;IView&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;event &lt;/span&gt;&lt;span style="color: teal"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: teal"&gt;TaskSearchEventArgs&lt;/span&gt;&amp;gt;&lt;br /&gt;        SearchRequested;&lt;br /&gt;    &lt;span style="color: blue"&gt;void &lt;/span&gt;ShowSearchResults(&lt;span style="color: teal"&gt;ICollection&lt;/span&gt;&amp;lt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;&amp;gt;);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: teal"&gt;ITaskRepository &lt;/span&gt;: &lt;span style="color: teal"&gt;IRepository&lt;/span&gt;&amp;lt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;&amp;gt;&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: teal"&gt;Task&lt;/span&gt;[] Search(&lt;br /&gt;        &lt;span style="color: teal"&gt;TaskSearchSpecification &lt;/span&gt;specification);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The view interface is derived from the IView interface, which I won’t explain in detail here.&amp;#160; Phil Haack did a good job of explaining this in his &lt;a title="Supervising Controller" href="http://haacked.com/archive/2006/08/09/ASP.NETSupervisingControllerModelViewPresenterFromSchematicToUnitTestsToCode.aspx" target="_blank"&gt;Supervising Controller example&lt;/a&gt; – IView holds common event handlers and methods for a web form (Init, Load, IsPostBack…)&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The repository interface is derived from IRepository, which holds CRUD methods used to persist objects.&amp;#160; I added the Search method to ITaskRepository.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Step 2: The SearchRequestEvent:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;For this step, I need to write 2 tests.&amp;#160; First, the Controller must listen (subscribe to) the View event so that it can respond.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;[&lt;span style="color: teal"&gt;Test&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;Verify_controller_attaches_to_search_requested_event()...&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Second, I want to make sure the Controller calls the Search method on the repository when this event is raised.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;[&lt;span style="color: teal"&gt;Test&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;Repository_search_method_should_be_called_in_response_to_search_request()...&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You may be asking why I left the guts of these tests out of this post?&amp;#160; After all, this is &lt;em&gt;supposed&lt;/em&gt; to be a post about writing tests first.&amp;#160; I struggled with this for a while and decided not to show the implementation here because I don’t want to get sidetracked trying to explain how to create mock calls to the view and to the repository.&amp;#160; One of the benefits of writing these tests is that I can do so &lt;em&gt;before&lt;/em&gt; I create any ASPX pages or database procedures.&amp;#160; Before I even have to think about ASPX or database connections and queries I can have a compiling project with unit tests that specify the behavior I want to achieve.&amp;#160; &lt;em&gt;That&lt;/em&gt; is the value I hope to convey in this post.&amp;#160; The test implementation details are available in the &lt;a title="NCharlie TDD exercise" href="http://code.google.com/p/ncharlie/source/browse/#svn/branches/tdd-demo" target="_blank"&gt;source for this exercise&lt;/a&gt;, and I hope to cover it in more detail in a later post.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;After implementing the above two tests correctly my project compiles, but when I execute the tests they fail (I’m using the &lt;a title="Jetbrains Resharper" href="http://www.jetbrains.com/resharper/" target="_blank"&gt;Resharper&lt;/a&gt; test runner to execute my test methods.&amp;#160; At boot-camp we used &lt;a title="TestDriven.Net Visual Studio Add-In" href="http://www.testdriven.net/" target="_blank"&gt;TestDriven.Net&lt;/a&gt;.&amp;#160; You can also execute these tests from NUnit’s console runner or the command line if you like.):&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_juzpwZu8gCw/SeEWGZzXp-I/AAAAAAAAABY/ApxPm_jmFzE/s1600-h/searcheventtestsfailed3.jpg"&gt;&lt;img title="SearchRequestedEvent tests failed" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="130" alt="SearchRequestedEvent tests failed" src="http://lh5.ggpht.com/_juzpwZu8gCw/SeEWGjfRqRI/AAAAAAAAABc/JQMiRpL6Eps/searcheventtestsfailed_thumb1.jpg?imgmax=800" width="525" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now I’ll write the code to make these two tests pass.&amp;#160; I create an event handler method (OnSearchRequested) that calls the repository’s Search method and attach it to the View’s SearchRequested event:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: teal"&gt;TaskManagerController&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: teal"&gt;ITaskManagerView &lt;/span&gt;_view;&lt;br /&gt;    &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: teal"&gt;ITaskRepository &lt;/span&gt;_repository;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public &lt;/span&gt;TaskManagerController(&lt;br /&gt;        &lt;span style="color: teal"&gt;ITaskManagerView &lt;/span&gt;view, &lt;br /&gt;        &lt;span style="color: teal"&gt;ITaskRepository &lt;/span&gt;repository)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;._view = view;&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;._repository = repository;&lt;br /&gt;        SubscribeViewToEvents();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;private void &lt;/span&gt;SubscribeViewToEvents()&lt;br /&gt;    {&lt;br /&gt;        _view.SearchRequested += OnSearchRequested;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;private void &lt;/span&gt;OnSearchRequested(&lt;br /&gt;        &lt;span style="color: blue"&gt;object &lt;/span&gt;sender, &lt;br /&gt;        &lt;span style="color: teal"&gt;TaskSearchEventArgs &lt;/span&gt;e)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: teal"&gt;ICollection&lt;/span&gt;&amp;lt;&lt;span style="color: teal"&gt;Task&lt;/span&gt;&amp;gt; tasks = &lt;br /&gt;            _repository.Search(e.SearchSpecification);&lt;br /&gt;        _view.ShowSearchResults(tasks);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now I rebuild and execute my unit tests:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_juzpwZu8gCw/SeEWGyJSVBI/AAAAAAAAABg/-z7jhOa4At4/s1600-h/searcheventtestspassed3.jpg"&gt;&lt;img title="SearchRequestedEvent tests pass" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="127" alt="SearchRequestedEvent tests pass" src="http://lh3.ggpht.com/_juzpwZu8gCw/SeEWGxEOC4I/AAAAAAAAABk/AUQiEjXlVY4/searcheventtestspassed_thumb1.jpg?imgmax=800" width="556" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;These two unit tests are passing now, and they don’t require an ASPX page or a database to do so.&amp;#160; I think that is valuable!&amp;#160; They clearly describe what the behavior of this part of my application should be and they enforce that behavior without requiring me to think about the specific implementation of the web form or the repository.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Next time I will continue with steps 3 and 4 in my task list above... testing and implementing the Search and ShowSearchResults methods.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;lt;&amp;lt; &lt;a title="Previous post in series" href="http://charliesolomon.blogspot.com/2009/04/design-driven-testing.html" target="_blank"&gt;Development Driven Tests (Introduction)&lt;/a&gt;, NCharlie: Test Driven Development, part 2 &amp;gt;&amp;gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1240921806131117404-3564327889465397499?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/3564327889465397499/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/ncharlie-test-driven-development-part-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/3564327889465397499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/3564327889465397499'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/ncharlie-test-driven-development-part-1.html' title='NCharlie: Test Driven Development – part 1'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_juzpwZu8gCw/SeEWGFf-hsI/AAAAAAAAABU/m3pD7mkOLfE/s72-c/taskmanagersearchsequence_thumb4.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1240921806131117404.post-3813334003829921762</id><published>2009-04-05T01:28:00.001-07:00</published><updated>2009-04-18T11:08:01.087-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>Development Driven Tests</title><content type='html'>&lt;p&gt;I’ve got the title &lt;strike&gt;wrong&lt;/strike&gt; backwards to make a &lt;strong&gt;point&lt;/strong&gt;.&amp;#160; That point is coming…&lt;/p&gt;  &lt;p&gt;I attended .Net &lt;a title="Headspring Systems" href="http://www.headspringsystems.com/services/agile-training/"&gt;boot-camp&lt;/a&gt; a few weeks ago and practiced &lt;em&gt;Test Driven Development&lt;/em&gt;.&amp;#160; We talked about new features to add to &lt;a title="codecampserver on google code" href="http://code.google.com/p/codecampserver/"&gt;CodeCampServer&lt;/a&gt; (the open source project used as the coding sandbox for class).&amp;#160; Our &lt;a title="Matt Hinze" href="http://mhinze.com/"&gt;instructor&lt;/a&gt; drew sequence diagrams on the whiteboard and we designed the interfaces and interactions the features would require.&amp;#160; We assigned tasks, wrote tests that failed, then wrote code to make them pass.&amp;#160; It was great.&amp;#160; To me it felt the way development should feel, like we were doing it &lt;em&gt;right&lt;/em&gt;.&lt;/p&gt;  &lt;p&gt;Now I’m trying to work through how to take these methods, which feel so right, back to work.&amp;#160; It seems to me this is a common problem developers like me deal with, and I want to succeed.&amp;#160; So I’ve been working through a project that uses the agile tools and methods demonstrated at boot-camp and tries to flesh them out using code that will run in our environment at work.&amp;#160; My thought was that a finished project, built using the methods I want to use more at work, would go a long way to convincing my coworkers of the value these methods and tools could bring.&amp;#160; Here are the ingredients I’m throwing together in my example project:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a title="Onion Architecture described by Jeffrey Palermo" href="http://jeffreypalermo.com/blog/the-onion-architecture-part-1/"&gt;Onion Architecture&lt;/a&gt; (to remove some pain caused by tightly coupled areas of our applications) &lt;/li&gt;    &lt;li&gt;Test First Development (using &lt;a title="NUnit" href="http://www.nunit.org/index.php"&gt;NUnit&lt;/a&gt; and &lt;a title="Rhino Mocks" href="http://ayende.com/projects/rhino-mocks.aspx"&gt;Rhino Mocks&lt;/a&gt;, to realize the increased code quality and code longevity that TDD proponents promise) &lt;/li&gt;    &lt;li&gt;Object-Relational-Mapping (using &lt;a title="NHibernate" href="http://nhforge.org/Default.aspx"&gt;NHibernate&lt;/a&gt;, because I’m tired of maintaining dozens (hundreds?) of stored procedures per app) &lt;/li&gt;    &lt;li&gt;Visual Studio 2005 (because that’s what we use at work) &lt;/li&gt;    &lt;li&gt;Web Forms using the &lt;a title="Phil Haack describes how to implement the Supervising Controller pattern using Web Forms" href="http://haacked.com/archive/2006/08/09/ASP.NETSupervisingControllerModelViewPresenterFromSchematicToUnitTestsToCode.aspx"&gt;Supervising Controller&lt;/a&gt; pattern (because we can’t use MVC at work and this seemed to me a testable next best thing for web forms) &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;I’ve named my project “NCharlie” for now, since it uses at least 3 technologies that start with “N”, and since it will help Charlie (me) chronicle my journey towards becoming a better programmer.&amp;#160; It is a simple task management tool (add to-do items, assign tags to them, search for them).&lt;/p&gt;  &lt;p&gt;This brings me back to the title of this post, and my &lt;strong&gt;point&lt;/strong&gt;.&amp;#160; After spending a few hours on NCharlie I’m realizing that it is &lt;em&gt;hard&lt;/em&gt; to make the transition to TDD.&amp;#160; I have been making the software work first for so many years now that it feels unnatural to write tests before implementations.&amp;#160; I am now at the point where I am writing more tests… but NCharlie is already working!!&amp;#160; I fell hard back into my old pattern and implemented the design before I wrote tests.&amp;#160; Sure I wrote a few tests up front, but I have to be honest with myself and admit they were only copies of tests I learned about at boot-camp.&amp;#160; Anything specific to the design of my test project has been just plain hard for me to test first.&lt;/p&gt;  &lt;p&gt;So is NCharlie just going to end up on my FAIL list?&amp;#160; I hope not… I still have plans for it (actually it is the reason I’m starting this blog.)&amp;#160; The way it sits now NCharlie is of no use to me or my coworkers because it doesn’t prove what I set out to prove… in fact it &lt;em&gt;disproves&lt;/em&gt; it!&amp;#160; &lt;/p&gt;  &lt;p&gt;This is a wake up call for me, because I think a developer who excels at writing tests first must also have a good handle on other principles/patterns that distinguish an average developer from a master.&amp;#160; So I’m average, that’s ok.&amp;#160; But I don’t want to stay here.&amp;#160; I have been reading books and blogs for a couple years now that describe pragmatic and agile methods I know I want to use in my career as a developer.&amp;#160; I’ve been successful with some, but the more I learn the more I realize how far I have to go.&lt;/p&gt;  &lt;p&gt;So here I go… new plan.&amp;#160; I’m going to dissect NCharlie and show you its ugly guts in public.&amp;#160; I have no shame.&amp;#160; In fact I’m quite proud of some of it… even though it is blatantly not original.&amp;#160; It uses code from open source projects like CodeCampServer and &lt;a title="Tarantino on google code" href="http://code.google.com/p/tarantino/"&gt;Tarantino&lt;/a&gt;.&amp;#160; I can however, take credit for getting them all working together from “File, New Project”.&lt;/p&gt;  &lt;p&gt;So let’s call this an introduction to a series of posts.&amp;#160; Mainly I want to document the process for my team at work and for myself so we can use this as we hopefully start to integrate these methods into our workflow (if I can get NCharlie to the point of proving its usefulness).&amp;#160; In the next post I’ll attempt to strip down NCharlie and start over at the point where TDD actually shows some benefit.&lt;/p&gt;  &lt;p&gt;Next in series -&amp;#160; &lt;a title="NCharlie: Test Driven Development – part 1" href="http://charliesolomon.blogspot.com/2009/04/ncharlie-test-driven-development-part-1.html"&gt;NCharlie: Test Driven Development – part 1 &amp;gt;&amp;gt;&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/1240921806131117404-3813334003829921762?l=charliesolomon.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://charliesolomon.blogspot.com/feeds/3813334003829921762/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/design-driven-testing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/3813334003829921762'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1240921806131117404/posts/default/3813334003829921762'/><link rel='alternate' type='text/html' href='http://charliesolomon.blogspot.com/2009/04/design-driven-testing.html' title='Development Driven Tests'/><author><name>Charlie Solomon</name><uri>http://www.blogger.com/profile/13639913903488849881</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
