July 2008 Entries

I recently added a new page to my application that is very similar in behavior to a page I had already completed a couple months back.  In fact, the similarities propagate down through the business and data layers as well.  Because of this, I was able to introduce some base classes to limit duplication.  In fact, I started all of this with a generic base test class.

All of the base classes (with generics) were pretty straight forward.  However, generics wouldn't help me when it came to the UI (aspx).  Master Pages would be the tool of choice.  Most of the time when we consider master pages, we think about common presentation, things like navigation and layout.  Being able to easily apply a common presentation template to all of your pages is awesome.  However, master pages go beyond that.

When I was looking at creating this new page, I resisted the urge to reuse code via copy-paste.  I looked at the page I had completed previously and identified those things I could abstract out.  It turns out that there were only a few.  So, I set off to create a master page, where the consumer would fill in the blanks.

I needed one ContentPlaceholder.  That part was easy.  What was a little more involved was getting the consumer of the master page to complete some of the needed behavior.  I decided to use events to allow the master "access" to the consumer.  I think this is a fairly common practice.

After refactoring, my master page was approximately the size (lines of code) of my original page and the original page only required 8 lines in the aspx, and 25 in the code-behind.  Of course, I tested everything multiple times along the way.  Finally, it came time to introduce the new page, which took me less than ten minutes.

If you have a similar need to use master pages for presentation "behavior",
here is a check list of things that might help:
  • Use the <%@ MasterType %> page directive so you do not have to cast Master to your type.
  • Public events on the master are a simple way to hook into the consumer.
  • Register handlers of the master's events in the consumer's Page_PreInit.  This will ensure that the handlers are in place plenty of time before the master needs them.
  • Think twice before you copy-paste your UI code.

Now, I'm going to go see if there are other pages in my application with similar behavior that can be improved with master pages.

In the application I've been working on, we have the requirement to handle unique constraint errors gracefully.  It wasn't really hard, I just had to check for OracleException.Code == 1.  The trick was the testing.  Testing a unique constraint error was not a problem.  The issue is verifying that other exceptions are bubbled up properly.

The basic exception handling code is like this:
    1 protected bool HasUniqueConstraintError( Action databaseAction )
    2 {
    3     try
    4     {
    5         databaseAction();
    6         return false;
    7     }
    8     catch( OracleException ex )
    9     {
   10         //Return true if exception is a Unique Constraint error
   11         if( ex.Code == 1 )
   12             return true;
   13         else
   14             throw;
   15     }
   16 }
Line 14 was not getting covered in my tests.  Also, because I have the same requirement under various scenarios, I decided to move this logic to a shared library.  The shared library doesn't deal with a real connection.  (And using a real connection wouldn't be a true unit test anyway).

So, I tried to test by instantiating OracleException myself.  Alas, OracleException has no public constructors nor any way to get a new instance.  On top of that it is sealed, so I cannot create a subclass either.  Then, I thought, maybe I can use serialization.  I tried XmlSerializer, but it requires a public parameterless constructor.

Finally, I landed on SoapFormatter.  Of course, I had to have a Soap message to start from.  So, I did a little bit of work to reproduce a unique constraint exception in the debugger.  I used SoapFormatter to serialize the OracleException and wrote it to a file.  Then, I added the file to my test project (as an embedded resource).  Finally, I wrote the test to use the embedded file and deserialze to an instance of OracleException I could throw.  I did the same for another common Oracle error (ORA-12154: TNS).  I verified that the unique constraint error was handled, while the TNS error was rethrown.

Here is the test code:
    1 [Test]
    2 public void TestHasUniqueConstraintError()
    3 {
    4     SoapFormatter formatter = new SoapFormatter();
    5     Assembly asm = Assembly.GetExecutingAssembly();
    6     using( Stream stream = asm.GetManifestResourceStream( "XCIS.Data.Nunit.OracleUniqueConstraintException.txt" ) )
    7     {
    8         OracleException testException = (OracleException)formatter.Deserialize( stream );
    9         Assert.That( HasUniqueConstraintError( () => { throw testException; } ) );
   10     }
   11 }
   12 
   13 [Test, ExpectedException( ExceptionType = typeof( OracleException ) )]
   14 public void TestHasSomeOtherOracleError()
   15 {
   16     SoapFormatter formatter = new SoapFormatter();
   17     Assembly asm = Assembly.GetExecutingAssembly();
   18     using( Stream stream = asm.GetManifestResourceStream( "XCIS.Data.Nunit.OracleTNSException.txt" ) )
   19     {
   20         OracleException testException = (OracleException)formatter.Deserialize( stream );
   21         HasUniqueConstraintError( () => { throw testException; } );
   22     }
   23 }
Tags: , , ,
Before I get started on Generic Classes in .Net, I'd like to give credit to CopySourceAsHtml for my new formatting of code blocks in my blog.  I made some of my own enhancements, but I'd still be using the boring black on yellow formatting without CSAH.  To learn how to get it to work on VS2008, go here (be sure to read the comments if you are running on XP).

We've all used generic classes without thinking much about it.  I use List<T> extensively (perhaps abusively).  I really only discovered the power of these classes when I started defining my own.  I was looking at my code and shaking my head over the duplication between my controller classes.  I finally decided to use inheritance and a template method to rid myself of this duplication.  I also introduced a base interface for my view that the base controller would interact with.  With a non-generic implementation, all of the subclasses would have to cast the view to the interfaces they each used.  With generics, no casting is necessary.  Just like List<T>, all of the properties and methods become strongly typed.

    1 public abstract class MyBaseController<T> where T : IMyBaseView
    2 {
    3     protected IMyDataContext _dataContext;
    4     protected T _view;
    5 
    6     public virtual void Observe( T view, IMyDataContext dataContext )
    7     {
    8         _view = view;
    9         _dataContext = dataContext;
   10 
   11         if( !_view.IsPostBack )
   12         {
   13             PreInitializeView();
   14             InvalidateCache();
   15             InitializeView();
   16         }
   17         else
   18         {
   19             UpdateView();
   20         }
   21         AttachEvents();
   22     }
   23     ...
   24 }

And an example of a concrete controller:

    1 public interface IMyConcreteView : IMyBaseView
    2 {
    3     event EventHandler Added;
    4     event EventHandler Edited;
    5     event EventHandler Deleted;
    6 }
    7 
    8 public class MyConcreteController : MyBaseController<IMyConcreteView>
    9 {
   10     protected override void AttachEvents()
   11     {
   12         base.AttachEvents();
   13         _view.Edited += new EventHandler( _view_InvalidationEvent );
   14         _view.Added += new EventHandler( _view_InvalidationEvent );
   15         _view.Deleted += new EventHandler( _view_InvalidationEvent );
   16     }
   17     ...
   18 }

Sure, I could have introduced a local "view" property that casts the base view to my concrete view, but do I really want to do that for all of my controllers.  I'm thankful that generics can do the work for me.

Tags: ,

Despite "The Cost of Premature Optimization", it's useful to know how things work so you can do basic things to avoid bad performance.  In the end, the example below is trivial and doesn't have a significant effect on performance (at least as it applies in my application).  However, knowledge of IEnumerable and yield can come in handy when it really counts.

As I was writing a bit of code, I wondered if order matters when it comes to query expressions.  Specifically, I have an enumerable that I was applying a Where and OrderBy to.  Ultimately, I was selecting the first item from the result.

I came back to the code later and decided to write an experiment.  What would happen if I changed the order of the Where and OrderBy?

   1: [Test]
   2: public void TestExperiment()
   3: {
   4:     IEnumerable<int> numbers = new[] { 3, 16, 2, 19, 14, 5, 20, 8, 11, 7, 13, 6, 12, 9, 4, 15, 10, 18, 1, 17 };
   5:     var composite = from number in numbers
   6:                     select new { Number = number, Date = DateTime.Today.AddMonths( number ) };
   7:     int whereCount;
   8:     int orderByCount;
   9:     Console.WriteLine( "IEnumerable<Anonymous> : Comparing Anonymous.Date" );
  10:     Console.WriteLine();
  11:     Console.WriteLine( "Compare <= " );
  12:     Console.WriteLine( "Where then OrderBy" );
  13:     whereCount = 0;
  14:     orderByCount = 0;
  15:     composite
  16:         .Where( test =>
  17:             {
  18:                 whereCount++;
  19:                 return test.Date <= DateTime.Today.AddMonths( 5 );
  20:             } )
  21:         .OrderBy( test =>
  22:             {
  23:                 orderByCount++;
  24:                 return test.Date;
  25:             } )
  26:         .First();
  27:     Console.WriteLine( "    Where:" + whereCount );
  28:     Console.WriteLine( "    OrderBy:" + orderByCount );
  29:     Console.WriteLine( "OrderBy then Where" );
  30:     whereCount = 0;
  31:     orderByCount = 0;
  32:     composite
  33:         .OrderBy( test =>
  34:             {
  35:                 orderByCount++;
  36:                 return test.Date;
  37:             } )
  38:         .Where( test =>
  39:             {
  40:                 whereCount++;
  41:                 return test.Date <= DateTime.Today.AddMonths( 5 );
  42:             } )
  43:         .First();
  44:     Console.WriteLine( "    Where:" + whereCount );
  45:     Console.WriteLine( "    OrderBy:" + orderByCount );
  46:     Console.WriteLine();
  47:     Console.WriteLine( "Compare >= " );
  48:     Console.WriteLine( "Where then OrderBy" );
  49:     whereCount = 0;
  50:     orderByCount = 0;
  51:     composite
  52:         .Where( test =>
  53:             {
  54:                 whereCount++;
  55:                 return test.Date >= DateTime.Today.AddMonths( 5 );
  56:             } )
  57:         .OrderBy( test =>
  58:             {
  59:                 orderByCount++;
  60:                 return test.Date;
  61:             } )
  62:         .First();
  63:     Console.WriteLine( "    Where:" + whereCount );
  64:     Console.WriteLine( "    OrderBy:" + orderByCount );
  65:     Console.WriteLine( "OrderBy then Where" );
  66:     whereCount = 0;
  67:     orderByCount = 0;
  68:     composite
  69:         .OrderBy( test =>
  70:             {
  71:                 orderByCount++;
  72:                 return test.Date;
  73:             } )
  74:         .Where( test =>
  75:             {
  76:                 whereCount++;
  77:                 return test.Date >= DateTime.Today.AddMonths( 5 );
  78:             } )
  79:         .First();
  80:     Console.WriteLine( "    Where:" + whereCount );
  81:     Console.WriteLine( "    OrderBy:" + orderByCount );
  82: }

Here are the results:

IEnumerable<Anonymous> : Comparing Anonymous.Date

Compare <=
Where then OrderBy
Where:20
OrderBy:5
OrderBy then Where
Where:1
OrderBy:20

Compare >=
Where then OrderBy
Where:20
OrderBy:16
OrderBy then Where
Where:5
OrderBy:20

What causes this phenomenon?  The use of IEnumerable and yield expressions.

A few things to consider when building query expressions:

  • What are the relative efficiencies of the Where and OrderBy algorithms?
  • Does the Where yield a relatively large result (as a percentage of the original data)?
    • Is the question above difficult to answer because of the dynamic nature of your data?
  • Would using negative logic change the result?
    • <= versus >
    • OrderBy versus OrderByDescending
  • Are the Where and OrderBy predicates related?
    • e.g.  You may filter by Status but order by Id
Can you think of other considerations?

I received quite a bit of feedback regarding my Join extension to IEnumerable that generates a comma separated list.  The purpose was to emulate string.Join(), but to add more flexibility as to what is "joined".

I took some time aside to investigate some of the suggestions and alternatives to my implementation.  I should note, that I would not normally do this.  I try to avoid premature optimization.  However, there are certain practices that can be learned that improve performance in general (like using a StringBuilder).  With these in your toolbelt, you can build better performing applications the first time, and at little or no cost.  It takes no more effort to use a StringBuilder than to concatenate strings in a loop (at least once you become familiar with StringBuilder).  Regardless, the intent of this post is not to try to sell you on StringBuilder.

Consider the fact that many optimizations involve tricks or algorithms that could make the intent of the method less obvious.  Unless you really need that optimization, are you really willing to pay the price?  Most of us do not work on applications that require lightning fast code.  In my career, the only times that I have had to address performance in my C# code was when I was working with bulk import and export of data.  In those cases, I was dealing with hundreds of thousands if not millions of iterations of my code.  In this case, a millisecond here or there can make a big difference.  On the other hand, saving a couple of milliseconds to pull up an ASP.Net page is hardly worth the effort.

The bottom line is, your customers will feel the effects of broken code before they recognize a couple of milliseconds added to their page load times.  In other words, don't sacrifice readability and maintainability for run-time optimization (unless you absolutely have to - even then, get a second opinion).

In response to a comment on one of my previous posts (Safely Overriding Equals), I've decided to post some comments about Extension methods in general.

A friend asked me what I gained out of extending string to convert to an enum.  He asked, "is it just 'syntactic sugar'?"  The simple answer is, "Yes."  In fact, this is true of many extensions.

Clearly, the major advantage of extensions is changing the syntax of how they are called.  In fact, I have written a few extensions that support chaining and make my code more readable (at least in my mind)

   1: OracleCommand command = GetProcedureCommand( "fas_closing_pkg.GetClosingDealLegs" )
   2:     .WithInputParameter( "pClosingMonth", closingMonth )
   3:     .WithInputParameter( "pDealId", dealId )
   4:     .WithOutputCursor( "oDealLegs" );

So what are extensions?  Well, they are just static utility methods with an extra keyword.  You can even call the extensions as if they are static methods if you really want to.

For Example:

   1: left.SafeEquals( right, test => left.Key == test.Key );
   2: GeneralExtensions.SafeEquals( left, right, test => left.Key == test.Key );

The second line is no different than what you might have written in .Net 2.0.  You may have even written many of these static utilties before and are now enabling them as extensions.

What this means, is that extensions are just static methods behind the scenes.  They are not instance methods.  One implication of this fact is that you must consider that the extended object could be null.  And, if it is null, you will not get an object reference exception when the extension is called.  You could get an exception within the scope of the extension method depending on your implementation.

For example, this is totally legal (generating no runtime errors)

   1: TestClass left = null; 
   2: TestClass right = new TestClass( "right" ); 
   3: Assert.IsFalse( left.SafeEquals<TestClass>( right, test => left.Key == test.Key ) );

Where SafeEquals is implemented as follows:

   1: public static bool SafeEquals<T>( this T left, object right, Predicate<T> test )
   2: {
   3:     if( left != null && right != null && right.GetType().Equals( left.GetType() ) )
   4:     {
   5:         return test( (T)right );
   6:     }
   7:     return false;
   8: }

So, when writing extensions, take a little extra time considering what would happen if the target object is null.