March 2008 Entries

In a recent post, I mentioned that I was working on a customized grid view.  Last week I completed the "binding to user settings" behavior.  Then I took a look at the source and was stunned.  I had almost 700 lines of code (and comments) and it wasn't well organized.  So, I set out to start adding regions to the code to separate different behavior.

The last sentence should have raised some flags.  Regions to separate behavior?  What happened to the cohesion of my grid view?  I seem to have lost sight of the Single Responsibility Principle.  What to do?  Well, I decided to refactor/redesign my enhanced grid view.  It really wasn't that hard to figure out how to split things up.  I had distinct enhancements that could easily(?) be extracted.  I decided to take an MVC approach, in particular a Supervising Controller.  If I were using MVC for my web pages, why not my web controls?

I focused on the row selecting behavior first.  I extracted any code related to row selection to a RowSelectorController.  Clearly, the code would not compile immediately.  I slowly went through all of the extracted code and started building up my RowSelectorView to resolve references to data proprietary to the GridView.  I simplified things by tightly binding the RowSelector to the GridView via a property on my view.  This allowed my controller direct access to all of the public members of the grid view (including a handful of events).  Then I only needed to add a little bit more to my view:

   1: public interface IRowSelectorView
   2: {
   3:     event EventHandler ChildControlsCreated;
   4:     event EventHandler PageSizeChanging;
   5:     event GridViewRowSelectingEventHandler RowSelecting;
   6:     event EventHandler AfterPreRender;
   7:  
   8:     GridView GridView { get; }
   9:     bool SelectOnRowClick { get; }
  10:     ListSelectionMode SelectionMode { get; }
  11:  
  12:     void PutInCache( string key, object data );
  13:     object GetFromCache( string key, object defaultValue );
  14:     void EnsureChildControl( Control child );
  15: }

In the end, there is more code than when I started, but it is much more manageable and testable.

Perhaps the most embarrassing thing about all of this is the total lack of TDD.  I've proven to myself that just about any behavior can be designed with testing in mind.  Server controls are no exception.  At least I can add some tests now that I have introduced my RowSelectorController.  Better late than never.

There are some key practices that all Agile developers should be familiar with.  In fact, "familiar" probably isn't strong enough.  One of these practices is test-driven development (TDD).  As one of the most important activities it ironically requires a lot of discipline.

What does Red-Green-Refactor mean?  Here is a brief description.  There are plenty of places to learn about TDD and Refactoring in more depth.
  • Red: write a test (it fails of course)
  • Green: write the least amount of code (the simplest thing that works) to make the test pass.
  • Refactor: rearrange your code to eliminate duplication and allow patterns to evolve.
I suppose TDD is still considered a change for me.  That is, it does not always come naturally and has not become my status quo.  To clarify, take these recent events.  I have been building a new website for my client.  I've taken an MVC approach and have started every new behavior with writing a test for my controller.  In fact, both my data layer and UI are abstracted through views so I can test my controller without the UI or the data.  So far, so good.

There are a couple of scenarios where my discipline is challenged.

First, is when I start building my UI and realize that there are a few things I need to tweak here and there.  It's very easy to change the behavior of my controller and quickly test it through the UI.  But wait!  What happened to Red-Green-Refactor.  I've totally gone off track.  The key here is that your mock or test view should expect the same behavior from the controller as the web implementation of the view.  So, if I consider changing behavior, I probably need to add or change a few tests first.  Also, what happened to Refactor?  Let's look at that with the next scenario.

The second scenario involves "timeliness".  What do you do when your boss (or client) is asking you to get something done and it looks like you may be short on time.  What activities do you sacrifice?  Well, if you are like me, you have a tendency to stop refactoring first; and if you still are short on time, you sacrifice testing as well.  As much as I know how dangerous this can be for future efforts, I still feel a huge tug away from TDD.  I have to constantly remind myself that the tests that I write and the refactoring I perform will show their benefits in the long run.  Still, it is very hard.

So what are some things I do to help me stay on track.  One thing I do is refactor less often.  That is I might go Red-Green-Red-Green-Red-Green-Refactor.  Sometimes this makes a lot of sense because you are constantly updating the same code and it is too early to recognize the patterns involved.  Something else I do is take larger TDD steps.  That is, I write a few tests, then I get them all to pass.  Typically I do this when I know all of the tests are related to the same method or set of methods.  Just keep in mind, that if you start to get a little lost, you are probably taking steps that are too large.

These are just a couple of things that help me stay true to TDD.  Do you have others?
Tags: ,

C# 3.0 has certainly introduced some really cool features.  I have used the Automatic Properties extensively as well as object and collection initializers.  These are real time savers.

However, the most exciting feature (IMHO) are Extension Methods.  My last post shows one example of how powerful extension methods can be.  Here is another example (inspired by Scott Gu).

   1: public static class Extensions
   2: {
   3:     /// 
   4:     /// Do not use this extension for large sets because it iterates through    
   5:     /// the entire set (worse case).  i.e. O(n).    
   6:     /// 
   7:     public static bool In<T>( this T test, params T[] set )
   8:     {
   9:         return set.Contains( test );
  10:     }
  11: }

Usage (excerpt from RowCommand event handler):

   1: if( e.CommandName.In( "Open", "Close" ) )
   2: {
   3:     ...
   4: }

Instead of:

   1: if( e.CommandName == "Open" ||    
   2:     e.CommandName == "Close" )
   3: {
   4:     ...
   5: }

Here is another example:

   1: public delegate T CreateIfNullDelegate<T>();
   2: public static T GetValue<T>( this System.Web.Caching.Cache cache, string key, CreateIfNullDelegate<T> createIfNullDelegate, bool updateCache )
   3: {
   4:     object value = cache[key];
   5:     if( value == null && createIfNullDelegate != null )
   6:     {
   7:         value = createIfNullDelegate();
   8:         if( updateCache )
   9:             cache[key] = value;
  10:     }
  11:     return (T)value;
  12: }
  13: ...
  14: myData = Cache.GetValue<MyType>( myKey, myCreateDelegate, true );
  15: myOtherData = Cache.GetValue<MyOtherType>( myOtherKey, myOtherCreateDelegate, false );

Instead of:

   1: object value = cache[myKey];
   2: if( value == null )
   3: {
   4:     value = myCreateDelegate();    cache[myKey] = value;
   5: }
   6: myData = (MyType)value;
   7: object value = cache[myOtherKey];
   8: if( value == null )
   9: {
  10:     value = myOtherCreateDelegate();
  11: }
  12: myOtherData = (MyOtherType)value;

I've used this extension repeatedly. A nice side effect is that the extension is more testable than a code-behind page.

How many times have you written something like this?

   1: public string MyStringProperty
   2: {
   3:     get
   4:     {
   5:         object s = ViewState["MyStringProperty"];
   6:         if( s == null )
   7:         {
   8:             s = "MyDefaultValue";
   9:         }
  10:         return (String)s;
  11:     }
  12:     set { ViewState["MyStringProperty"] = value; }
  13: }
  14:  
  15: public bool MyBooleanProperty
  16: {
  17:     get
  18:     {
  19:         object b = ViewState["MyBooleanProperty"];
  20:         if( b == null )
  21:         {
  22:             b = true;
  23:         }
  24:         return (bool)b;
  25:     }
  26:     set { ViewState["MyBooleanProperty"] = value; }
  27: }

Wouldn't this be better?

   1: public string MyStringProperty
   2: {
   3:     get { return ViewState.GetValue( "MyStringProperty", "MyDefaultValue" ); }
   4:     set { ViewState["MyStringProperty"] = value; }
   5: }
   6:  
   7: public bool MyBooleanProperty
   8: {
   9:     get { return ViewState.GetValue( "MyBooleanProperty", true ); }
  10:     set { ViewState["MyBooleanProperty"] = value; }
  11: }

With C# 3.5, all you need is this extension method

   1: internal static class MyExtensions
   2: {
   3:     public static T GetValue<T>( this IDictionary bag, string key, T defaultValue )
   4:     {
   5:         object value = bag[key];
   6:         if( value == null )
   7:         {
   8:             value = defaultValue;
   9:         }
  10:         return (T)value;
  11:     }
  12: }

Cheers

So, I decided to put my custom GridView in a AJAX UpdatePanel.  This was my first serious exposure to AJAX (asp.net).  I discovered that I was having major problems.  I kept getting an "unspecified error".  Because it occurred whenever I clicked on a row, I assumed it had to do with the JavaScript I wrote for the GridView.  I did tons of web searching about how to properly use script in a controlled wrapped in an update panel.  I tried a few things, including embedding the script and registering the script with the script manager.  None of this helped.  I finally found a forum post that said something about malformed html.  So, i decided to check out what I was rendering.  Because my control added a hidden field, It ended up rendering the <input> along with the <table> inside a <div>.  I also had the whole thing (including my update panel) wrapped in a <p>.  After cleaning this up, amazingly everything was peachy.  I suppose I can thank ASP.NET AJAX for making me more honest with my HTML.

The "sorta" in the title is in reference to some work I did a few years ago (2003-04).  A colleague of mine started implementing a rough form of AJAX.  Basically he wrote his own framework and we continued to enhance it to meet our needs.  He was certainly ahead of the curve.  The applications we were able to develop were quite nice.  We had our own tree view that could get additional branch data without refreshing the screen.  The only caveat was that we really couldn't mix server-side controls with our brand of AJAX.  In fact, we had a lot of JavaScript and a handful of aspx pages that simply returned XML responses.  It was quite elegant.  Well done Andres.

A few nights ago I decided to work on a custom GridView.  My goal (derived from a customer request) was to build some additional behavior into the GridView without having to write a whole bunch of client-side or code-behind code.

These are the things that I am focusing on:
  • Select a row when the user clicks on it (no CheckBox)
  • Allow the user to select multiple rows (using Shift and Ctrl keys)
  • Bind display settings to site preferences
    • Column header text
  • Bind display settings to user preferences
    • Columns to display
    • Column order
    • Default sort

I got the first two working quite well.  I have tested in IE7 and FireFox.  For selecting a row OnClick I simply overrode the CreateRow method of GridView and added the "Select$n" command postback reference to the row.

To select multiple rows, took a bit more work.  I added a child HiddenField to identify which keys where pressed when the user clicked on the row.  I wrote a JavaScript function to capture the shiftKey and ctrlKey values from the event metaobject.  I defined the OnClick for the row to call this function prior to the postback reference.  Then, I overrode the RaisePostBackEvent method to change how the "Select" command works.  Needless to say, there is a lot more to take care of to make the selections persist between postbacks and to determine when to clear the selections.

Normally, I would be glad to share my code.  However, as I am developing this on behalf of my employer, I cannot.  I searched for something like this before I tried to reinvent the wheel.  I simply took bits and pieces of what I saw other people doing and made work for me.

As an interesting side note.  For a former employer I implemented multi-select purely client side.  It worked great except that it was difficult (near impossible) to persist the selections after a round trip to the server.  I think the next step/test is to AJAX enable my GridView and make selection changes without a full page refresh.

I encountered a problem with Oracle's MERGE DML yesterday.  What I was trying to do is use the "Upsert" feature of the merge, except with no distinct source.  All of the searching I did for the MERGE showed examples of how to merge data from a source into a target.  In my case, I thought the source and the target were the same.

This is what I initially tried.  It didn't work.  It turns out that if the source (s) returns an empty set, then the merge is effectively a no-op

   1: merge into MY_TARGET t
   2: using (select *
   3:          from MY_TARGET
   4:         where COL1 = :p1
   5:           and COL2 = :p2) s
   6:   on (t.COL1 = s.COL1 
   7:   and t.COL2 = s.COL2)
   8: when matched then
   9:   update set t.COL3 = :p3
  10: when not matched then
  11:   insert (COL1, COL2, COL3) 
  12:   values (:p1, :p2, :p3)

I thought for a while to figure out how I could get the source to always return something.  Finally it occurred to me that I could use DUAL.

   1: merge into MY_TARGET t
   2: using (select 1 from DUAL) s
   3:   on (t.COL1 = :p1 
   4:   and t.COL2 = :p2)
   5: when matched then
   6:   update set t.COL3 = :p3
   7: when not matched then
   8:   insert (COL1, COL2, COL3) 
   9:   values (:p1, :p2, :p3)

I hope this helps someone else.  At the very least it will be here the next time I need it.

Tags: , ,

It seems that I have encountered a scenario where many aspects of C# 2.0+ come into play.  I needed to handle conversion from an IDataReader.GetValue() result to a generic type, including Nullable<>.  It took me a while to figure it out and with a little help from this snippet, I was quite please with the result.  So, I decided to share.

   1: private static T NullValue<T>( object testValue, T nullValue )
   2: {
   3:     T returnValue;
   4:     if( testValue is DBNull )
   5:     {
   6:         returnValue = nullValue;
   7:     }
   8:     else if( typeof(T).GetGenericTypeDefinition().Equals( typeof(Nullable<>) ) )
   9:     {
  10:         returnValue = (T)Convert.ChangeType( testValue, Nullable.GetUnderlyingType( typeof(T) ) );
  11:     }
  12:     else
  13:     {
  14:         returnValue = (T)Convert.ChangeType( testValue, typeof(T) );
  15:     }
  16:     return returnValue;
  17: }

It turns out that System.Decimal cannot be converted directly to Nullable<Int32>.  However, it can be converted to Int32, which can be converted to Nullable<Int32>.  I guess the transitive property doesn't apply to type conversion.

I recently switched over to TeamCity from CC.Net.  Normally, like most people, I hate change.  But, while it took me a while to figure out the new way of configuring a build, it was certainly worth it.

Some of the benefits:
  • Being able to copy a configuration to easily start configuration of another.
  • Nifty "pause" feature.
  • Taking responsibility for broken builds
  • Easily viewing build logs in the web interface
  • Client tools (tray, ide integration)

I am sure there are others.  The list above are benefits that I have seen and used firsthand.  Unfortunately, there are drawbacks as well, for example, difficulty supporting NCover and Test results at the same time.  JetBrains has stated that they plan to support their own coverage metric in the not-so-distant future.

I introduced my boss to Subversion recently, and of course he almost immediately had a problem.  It turns out that as soon as he added his computer to a different domain Tortoise wasn't working on his existing projects.  My guess is that it had to do with the Computer Name change.

The symptoms he saw as that Tortoise was not recognizing that he made changes.  In fact, neither the Update nor the Commit menu options were showing up.  Tortoise was clearly confused.  We tried a number of things including using the Tortoise installer "repair" option.  Nothing seemed to work.  Finally, I had him back up his changes (fortunately only a couple of folders) and start over:
  1. Backup entire source
  2. Delete everything under the project root (including _svn)
  3. Check out the project again
  4. Copy source back (not the old _svn)

After following this process, he was back up and running.  We are still puzzled as to what the problem was.  Clearly this solution is not ideal if you have a lot of subfolders (each with it's own _svn).  Hopefully, we never encounter this issue again.

So, I learned something new yesterday.  I haven't spent a lot of time in ASPX over the last couple of years, and when I did I didn't really pay attention to what is new.  Well, I learned about the nifty DataSourceControl classes.  In my case, I decided to use the LinqDataSource control.  And I put it to use on a nested GridView with Paging

I struggled for more hours than I care to admit to accomplish what I wanted.  In the end, I was pleased at just how little code was required.  Check out my full article on the event.  I hope it can save someone else a little time.

Cheers,
Will