Thursday, October 20, 2005

How exciting!  Today I received an email from Microsoft indicating that I have been nominated for an MVP award!  We'll see what happens come January!

Thursday, October 20, 2005 6:07:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback

Call be masochistic, but I kinda like nuts and bolts programming. I enjoy finding ways of optimizing the code that runs (or should run) completely behind the scenes that provides the infrastructure and support to the “higher-level” objects - whether they be business object, UI, etc.  How many out there use data readers, like the SqlDataReader, OracleDataReader?  Like almost everything, they have their place and are frequently (mis)used.  I fear that many developers (myself included) have written code similar to the following in a generic 'data access' class:

internal sealed class DataAccess {
   internal static SqlDataReader QueryReader(SqlCommand cm) {
      // ...include the code to retrieve data reader and return it
   }
}

internal sealed class EntryPoint{
   public static void Main() {
      using ( SqlDataReader dr = DataAccess.QueryReader(cm) ) {
         // ...iterate over the reader, utilizing the data
      }
   }
}

This, I believe, is a fairly common pattern that developers employ.  Is there anything wrong with it?  Well, I think there is.

First of all, there are no constraints on how the data reader is used and consumed.  In other words, the consumer class (in this case the EntryPoint class) could hold on to the SqlDataReader indefinitely and thereby maintain a connection to the database indefinitely (the data readers are tied to a connection to the database).

Second, your code that walks the reader's contents is tightly coupled with the flow of the procedure.  You have to include your code to iterate the reader inline (or at least delegate the reader to a worker method) to extract the data and it's not as reusable in that fashion.

So what's a better solution?  What do I propose?  Well, I prefer a slightly different approach to how a data reader gets passed back to a consumer that puts tighter constraints on the lifetime of the reader (though it can still be abused by malicious code) and helps compartmentalize how a reader is used.

I suggest using a delegate (a safe function pointer class) as a vehicle to return the data reader out.  Let's take a look at a simple example and then discuss how to augment the functionality to make it even better, and then we'll step it up a notch for .NET 2.0 and we'll leverage some functionality therein.  On to the simple example.  Disclaimer: Please bear in mind that there are other methodologies and objects that provide an ability to get data from a database in a disconnected manner (from DataSets, XmlSerialized objects, etc).  I am merely illustrating how I prefer to work with readers in a semi-practical example.

Simple Example:

Here we create a delegate that has as its parameter a data reader.  The consumer sets up the function to be called via the delegate and then invokes the data access class.  The data access class then takes a command object, executes it, and then calls the callback function (via the delegate), passing the reader.  Once the callback function finishes, resources are cleaned up and the data access method exits.

internal delegate void DataReaderCallback(IDataReader dr);

internal sealed class DataAccess {
   private static SqlConnection getConnection() {
      SqlConnection cn = new SqlConnection(“Data Source=(local); Initial Catalog=AdventureWorks; Integrated Security=SSPI“);
      cn.Open();
      return cn;
   }

   internal static void QueryReader(SqlCommand cm, DataReaderCallback callback) {
      SqlConnection cn = getConnection();
      SqlDataReader dr = null;
      try {
         cm.Connection = cn;
         dr = cm.ExecuteReader(CommandBehavior.CloseConnection);
         if ( null != callback ) callback(dr);
      }
      finally {
         if ( null != dr ) ((IDisposable)dr).Dispose();
         if ( null != cn ) cn.Dispose();
         cm.Connection = null;
      }
   }
}

internal sealed class Worker {
   private ArrayList _list;

   internal void DoWork() {
      SqlCommand cm = new SqlCommand(“SELECT Name FROM Production.Product WHERE Name LIKE '[A-C]%' ORDER BY Name“);
      DataAccess.QueryReader(cm, new DataReaderCallback(this.callbackFn));
      for ( int i = 0; i < _list.Count; i++ )
         Console.WriteLine(“{0}: {1}“, i, _list[i]);
   }

   internal void callbackFn(IDataReader dr) {
      _list = new ArrayList();
      while ( dr.Read() )
         _list.Add(dr.GetString(0));
   }
}

This solution is pretty slick but it has, in my eyes, a drawback: there must be shared state between the callback function and the method that invokes the reader.  Namely, in this example, the ArrayList that gets built in the callback function.  Let's make a simple enhancement to our delegate and see where that takes us:

Enhanced Example:

This example has a delegate that not only passes the data reader, but returns an object.  In effect, the data access method will call back on the delegate which will build up its state and return it.  The return value is then forwarded on to the consumer as the return value from the QueryReader method as a populated object.

internal delegate object DataReaderCallback(IDataReader dr);

internal sealed class DataAccess {
   private static SqlConnection getConnection() {
      SqlConnection cn = new SqlConnection(“Data Source=(local); Initial Catalog=AdventureWorks; Integrated Security=SSPI“);
      cn.Open();
      return cn;
   }

   internal static void QueryReader(SqlCommand cm, DataReaderCallback callback) {
      SqlConnection cn = getConnection();
      SqlDataReader dr = null;
      try {
         cm.Connection = cn;
         dr = cm.ExecuteReader(CommandBehavior.CloseConnection);
         return ( null != callback ) ? callback(dr) : null;
      }
      finally {
         if ( null != dr ) ((IDisposable)dr).Dispose();
         if ( null != cn ) cn.Dispose();
         cm.Connection = null;
      }
   }
}

internal sealed class Worker {
   internal void DoWork() {
      SqlCommand cm = new SqlCommand(“SELECT Name FROM Production.Product WHERE Name LIKE '[A-C]%' ORDER BY Name“);
      ArrayList list = DataAccess.QueryReader(cm, new DataReaderCallback(this.callbackFn)) as ArrayList;
      for ( int i = 0; i < list.Count; i++ )
         Console.WriteLine(“{0}: {1}“, i, list[i]);
   }

   internal object callbackFn(IDataReader dr) {
      ArrayList list = new ArrayList();
      while ( dr.Read() )
         list.Add(dr.GetString(0));
      return list;
   }
}

Note, the major change here is that there is no build up of shared state and once the QueryReader returns, the caller has a fully disconnected set of data with which to work.  This is all well and good, and in fact, I very much like this approach.  It gives the caller the ability to write lean, tight code and still interact with the data reader in the fashion the caller wants.  Very cool.  However, as you may quickly realize, there's no real constraint set on the type of object returned from the callback function - it's an object.  Therefore, the caller has to know the type of object to be returned (in this case, an ArrayList).

With the looming advent of .NET 2.0, there's a very slick way to ensure type compatibility and strongly typed and verified method signatures: generics.  Let's explore a technique that mimics the example above, but utilizes generics to accomplish the goal.

.NET 2.0 Generic Example:

internal delegate T DataReaderCallback<T>(IDataReader dr);

internal static class DataAccess {
   // same getConnection() method as above

   internal static T QueryReader<T>(SqlCommand cm, DataReaderCallback<T> callback) {
      SqlConnection cn = getConnection();
      SqlDataReader dr = null;
      try {
         cm.Connection = cn;
         dr = cm.ExecuteReader(CommandBehavior.CloseConnection);
         return ( null != callback ) ? callback(dr) : default(T);
      }
      finally {
         if ( null != dr ) dr.Dispose();
         if ( null != cn ) cn.Dispose();
         cm.Connection = null;
      }
   }
}

internal sealed class Worker {
   internal void DoWork() {
      SqlCommand cm = new SqlCommand(“SELECT Name FROM Production.Product WHERE Name LIKE '[A-C]%' ORDER BY Name“);
      ArrayList list = DataAccess.QueryReader(cm, new DataReaderCallback<ArrayList>(this.callbackFn));
      for ( int i = 0; i < list.Count; i++ )
         Console.WriteLine(“{0}: {1}“, i, list[i]);
   }

   internal ArrayList callbackFn(IDataReader dr) {
      ArrayList list = new ArrayList();
      while ( dr.Read() )
         list.Add(dr.GetString(0));
      return list;
   }
}

Notice that pretty much all of the code is identical except for a few things:

  • the delegate signature is generic; you must specify the type return type upon creation.
  • the QueryReader signature is also generic, but the type only needs to be specified on the DataReaderCallback parameter instance.
  • we can longer return null from the QueryReader method, but instead return default(T), because the method is generic.
  • you no longer need to cast a DataReader to IDisposable to call Dispose. Ok, that was unrelated, but I like the enhancement.

All in all, I really like this approach.  It makes consuming data readers much more specific, isolated, wrapped, and contained, and empowers the data access layer in ensuring that its resources are properly cleared.

Happy coding!

Thursday, October 20, 2005 6:00:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Thursday, October 13, 2005

Today's Utah .NET User Group Meeting was quite a success.  We had about 50 people show up for Fabio Cavalcante's presentation on LINQ, DLINQ, and XLINQ and Robert Love's presentation on Extending ASP.NET.  This month's meeting was sponsored by The MindCenter, a local training and education organization.  We had some great giveaways too, including a 25 CAL copy of Windows Server 2003!

What started out to be an extremely stressful and jam-packed day ended on a very long-overdue high note.

Thanks guys for such a good job!

Thursday, October 13, 2005 5:02:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

This may be old news, but I just discovered it and thought it was hilarious.  On browsing Sysinternals.com, I stumbled upon the BlueScreen Screen Saver.  This would be an excellent screen saver to install on your boss's computer! :)  It is most convincing!

Thursday, October 13, 2005 3:37:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

In light of my recent (and quite repetitive) fiascos with respect to domain controllers, I've decided to make a change.  And despite of the fact that I absolutely love what an AD domain offers, I've decided to forego the whole notion of having a domain.  The primary motivation behind the switch is the fact that with a domain things are so closely interrelated and coupled and a loss of the DC has more impact across all other computers on the network.

If I were to set up the domain, then I'd have to end up having to reset the entire domain in two weeks when my drive crashes again (ok, slight exaggeration...three weeks).  Technically, of course, I could simply restore my System State backup, but frankly I just can't bring myself to trust a backup made from a drive replete with bad sectors.  It will be a lot easier to simply reset a single machine rather than a domain and manage all of the domain memberships.

Ok, now to the real reason I'm writing this post in the first place:

Another change that I'm implementing is that Exchange will not be part of reconfiguration.  Don't get me wrong, Exchange is awesome and I love it, but it's too difficult for me to rebuild a domain and restore all my mailboxes and Exchange settings, and get everything else up and running - I typically lose a day and lots of hair.  OWA has been an absolute treat.  I'm looking at replacing my email server, therefore, with something smaller, easier to deploy, and Microsoft-technologies based (.NET-based is a plus).

To help assuage the pain of not having my DC and Exchange running, I've installed a trial version of EmailArchitect Email Server 5.9 on my laptop and routed all SMTP traffic to it in the interim until my server is back up and online.  I'm really digging it.  It has a nice web-based front end as well as full SMTP/POP3/IMAP4 support - Outlook setup was a breeze.

Does anyone have any recommendations for an Email Server they prefer to run?  I'm looking for a cost effective, multi-domain, multi-mailbox-supporting email system that is stable, works well, and all that jazz.  Suggestions are very welcome!

Thursday, October 13, 2005 1:54:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, October 12, 2005

Can someone please tell me (teach me) how to not lose a domain controller hard drive?  I am practically in tears in frustration because yet again my domain controller disk went bad, sour, kaput.  I rebooted after running into a few issues (namely not being able to run Regedit.exe (or RegEdt32.exe) and attempting to perform a System State backup but erroring out because it couldn't write to the event log) and was greeting with the all too pleasant to behold phrase:

Non-System disk or disk error
replace and strike any key when ready

What is this? the fourth time this year? fifth? I've lost count.  I've used different disks, different computers, different outlets...I ALWAYS get bad sectors and/or corrupted data and/or crashed drives on my domain controller computers.  I am fed up with it.  I am yet again faced with rebuilding my domain - mostly because I don't have (anymore) any trust in my System State backups - who knows? maybe I backed up corrupted data.  I sure don't want to restore it.  On the bright side, my domain controller is just one computer and there is only one other member server...but lots'o'workstations.

I must be doing something wrong.

I should've been a farmer.

Wednesday, October 12, 2005 11:07:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Monday, October 10, 2005

For those ardent followers of my blog, my apologies for the noticeable absence.  I've been out of town working on the next release of our software package and that has consumed every single waking hour (from 8:00 AM to 3:00 AM) each day.  So while I've had a mountain of ideas and concepts to blog about, time has simply not been on my side.  Today, however, I find myself with at least a breather.

I'd like to comment on a concept that I actually formulated and coded a few months ago, and always meant to blog about but simply never got around to it.  In fact, a little over a year ago, I blogged about serializing a class from Xml to simplify reading Xml elements from a .config file.  The issue with this particular approach is that the data doesn't roundtrip; that is, the Xml doesn't ever get written back to the .config file.  Sure, there are ways to do it, but my solution didn't address any of those techniques primarily for one reason: I'm not of the camp that believes the .config file is for storing user preferences.  Config files are there so that an application can be tweaked to operate with a set of predefined, but customizable parameters.  They are designed to be set before the application starts and remain that way.

User settings, on the other hand, are preferences that the user may customize from usage to usage.  For that we need a mechanism separate from, but sort of related to, the .config file.  If you're a user of the .NET 2.0 Framework, you may already be familiar several of the new classes in the System.Configuration namespace (namely the SettingsBase, SettingsProperty, SettingsProvider, et al).  These classes facilitate a very similar set of functionality to what I've created, but using a different mechanism.

The primary issue I set out to resolve for my particular applications was that I wanted to create a mechanism through which I could load and read user settings/preferences and simultaneously be able to persist their preferences back.  This is pretty trivial in concept but there can often be hiccups and gotchas in real world scenarios.  For example, suppose your application is being run in a heightened security zone (such as the intranet or internet zones).  If you want to persists user settings to disk you can't simply open a pipe to the root c:\ and start writing.

To this end I created a set of utility classes that encapsulate the logic needed to store and retrieve settings.  The principal class is called UserSettingsManager.  The UserSettingsManager is responsible for loading, saving, and deleting user settings.  For simplicity (both in implementation and details) I chose to rely on XmlSerialization for the storage of user settings.  I also decided to store user settings using the System.IO.IsolatedStorage classes for a few reasons:

  • The location of the file is something I don't have to worry about (the OS handles that for me) - all I care about is the file name - even then, I only care that the information can be retrieved and persisted.
  • I can make settings roaming if roaming profiles are used.  That way, settings move with the user as he logs in to different computers.
  • The files are just that: isolated.  I can only see settings pertaining to my application/user.  Isolated storage is a protected (but not encrypted) storage mechanism when using managed code.
  • Users/applications with lower privileges on the system can still save and read data from IsolatedStorage.

In order to make it generic, I also created a marker interface (that is, an interface with no methods) called IUserSettings.  Implementing classes can then be passed to the Save() method or retrieved via the Load() method in a generic way.  Classes that implement this interface should also support XmlSerialization (possibly using XmlRootAttribute and friends) because the UserSettingsManager relies on XmlSerialization to save and read the settings.

Lastly, I created an attribute called UserSettingsFileAttribute that can applied to the assembly.  This attribute simply designates the name of the file used for storing user settings.

That's pretty much all there is to it.  Here's a little run down of the code (without comments).  You can download the full source complete will comments here.

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple=false)]
public sealed class UserSettingsFileAttribute : Attribute {
   private string _settingsFile;

   public UserSettingsFileAttribute(string settingsFile) {
      _settingsFile = settingsFile;
   }

   public string SettingsFile {
      get { return _settingsFile; }
   }
}

///////////////////////////////////////

public interface IUserSettings { }

///////////////////////////////////////

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Reflection;
using System.Xml.Serialization;


public sealed class UserSettingsManager {
   private UserSettingsManager() { }

   public static void Save(IUserSettings settings) {
      Type type = settings.GetType();
      XmlSerializer ser = new XmlSerializer(type, string.Empty);

      using ( IsolatedStorageFile file = getIsolatedStore() )
      using ( IsolatedStorageFileStream fs = new IsolatedStorageFileStream(getFileName(type), FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, file) ) {
         ser.Serialize(fs, settings);
         fs.SetLength(fs.Position);
      }
   }


   public static IUserSettings Load(Type type, out bool createdNew) {
      XmlSerializer ser = new XmlSerializer(type, string.Empty);
      IUserSettings settings;

      try {
         using ( IsolatedStorageFile file = getIsolatedStore() )
         using ( IsolatedStorageFileStream fs = new IsolatedStorageFileStream(getFileName(type), FileMode.Open, FileAccess.Read, FileShare.Read, file) ) {
            settings = (IUserSettings)ser.Deserialize(fs);
            createdNew = false;
         }
      }
      catch ( FileNotFoundException ) {
         ConstructorInfo ci = type.GetContructor(Type.EmptyTypes);
         settings = (IUserSettings)ci.Invoke(null);
         createdNew = true;
      }

      return settings;
   }


   public static void Delete(IUserSettings settings) {
      Type type = settings.GetType();
      using ( IsolatedStorageFile file = getIsolatedStore() )
         file.DeleteFile(getFileName(type));
   }


   private static string getFileName(Type type) {
      Assembly asm = Assembly.GetAssembly(type);
      object[] usfa = asm.GetCustomAttributes(typeof(UserSettingsFileAttribute), false);
      if ( null == usfa || 0 == usfa.Length )
         throw new ArgumentException(“Unable to resolve the user settings file name.  UserSettingsFileAttribute is undefined.“);
      else
         return ((UserSettingsFileAttribute)usfa[0]).SettingsFile;
   }


   private static IsolatedStorageFile getIsolatedStore() {
      return IsolatedStorageFile.GetStore(IsolatedStorageScope.Assembly | IsolatedStorageScope.User, null, null);
   }
}

Once incorporated into your project or into a utility assembly you can implement it as simply as the following:

[XmlRoot("userSettings")]
public sealed class UserSettings : IUserSettings {
   // ...
   // ...

}

// when you need to load the user settings:
private UserSettings _settings;
bool createdNew;
_settings = UserSettingsManager.Load(typeof(UserSettings), out createdNew) as UserSettings;
if ( createdNew ) UserSettingsManager.Save(_settings);

// when you need to save the settings:
UserSettingsManager.Save(_settings);

It's as simple as that and it sure makes managing user settings a snap!

Monday, October 10, 2005 4:37:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, September 28, 2005

Well, I'm pretty excited.  Today I received my set of Bose QuietComfort 2 headphones.  They're a bit on the pricey side, so I've been saving for a while, but I am in love with them!  The phones are awesome and extremely comfortable.  Even before putting in the single AAA battery it drowned out so much of the ambient sound I could tell I was in for a treat.

Then I put the battery in, switched 'em on and I was in an auditorium.  Perhaps appropriately, the very first musical piece I played once putting them on was Beethoven's 9th Symphony :-)

The phones fold down nicely and came with a great hard carrying case which makes them perfect for traveling.  In fact the primary reason I purchased them in the first place was because of travel.  I travel quite frequently and airplane noise really wears on me.  That, and the fact that I had to turn my speakers up very high just to hear over the engine howlings, thereby adding more noise and stress prompted me to look into a noise-cancelling alternative.  Though I haven't yet taken these on a test flight (I will on Sunday), I can tell that I'm already going to be very pleased with the result.

Wednesday, September 28, 2005 4:39:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, September 27, 2005

I've been dabbling with Windows Vista Beta 1...and I must say “WOW!”  I'm loving it!  I took a bit of time late this afternoon to set up a Virtual PC image with the Vista Beta 1 bits on it.  It took some some to get it up and running, so I thought I'd provide some info to ease your burden of setting it up.

First of all, when installing Vista it will not be able to recognize or identify a hard drive that you create with Virtual PC right out of the box, you need to do some configuring.  This is made manifest part way through the installation when is asks you into which drive you want to install the OS and it will not allow you to select the partition or format it, saying that it's unavailable.  Any attempt to repair this will result in failure.  What you need to do is drop down into the Windows PE (Preinstallation Environment) console by pressing SHIFT+F10 after selecting to 'Install Windows'.

Apparently, what happens is when you set up your disk (I chose to create a fixed 16GB drive image, though I don't think that's required) it will create the image to the proper size, but the installer will think that there are 0 bytes free on the disk for some reason. Once in the console, you'll need to blow away (CLEAN) the partition, and recreate a RAW partition and format it.  After that, the installer will locate the disk during install and it's off to the races....very slow races mind you, but races nonetheless.  The installer will take over an hour and a half (possibly much more).  So here's what you do (at least this is what I did and it worked):

At the PE console type in the following commands, pressing [ENTER] between each one:

  • DISKPART

DISKPART will drop you down to a DISKPART command line where you enter the following commands:

  • SELECT DISK 0
  • CLEAN
  • CREATE PARTITION PRIMARY
  • ACTIVE
  • FORMAT
  • ASSIGN LETTER=C

Then restart the installation (perhaps rebooting via Action menu -> CTRL+ALT+DEL option).

Once Windows is installed (which will take a considerable amount of time, but which is also completely “hands off” once started), it will not be able to detect drivers for the devices, and will render the UI in 4-bit color at 800x600.  You'll need to install the Virtual Machine Additions via the Action menu.

I must say, that I am incredibly impressed with not only the UI, but how clean and professional it is.  Windows Vista is going to rock, I'm so excited!  I'm gonna start putting it through it's paces to see how my apps work on it and start checking it out.

Tuesday, September 27, 2005 3:07:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

In all our copious free time we've had a chance to do a few upgrades to the house.  We built a shed and finished it, replaced the garage door, etc.  Recently, we took the courtyard/patio in the front yard and finally enclosed it in a brick wall with columns and lights.  I say finally because it was kind of an eyesore for about a year...we had the courtyard poured with 3/4” PVC pipe sticking up out of it in three places.  When we had the concrete poured we preanticipated that the wall would be built so I ran the pipe underneath the stairs into the basement and then up and out where the columns were to be.  It feels so much better now to have it completed.  I have a few pictures that show the work in progress and the finished product.  The last remaining task is to run the wire up the wall to the switch to the lights on the posts switch on with the other exterior lights.  Oh, and that reminds me, I also changed out the lights on the house (I hated the stock lights that were included with the house)...these are so much nicer.

In Progress:


And the finished wall:

Tuesday, September 27, 2005 4:22:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Sunday, September 25, 2005

At some conference, IBM was showcasing their voice recognition system, and issuing voice commands on the command prompt of his laptop.

Someone from the first row shouted out "format c:"

Someone else shouted out "return; yes; return"

(credit to Juan, commentor on ASP.NET Resources - What Goes Around Can't Be Recalled)

Sunday, September 25, 2005 2:14:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback