Monday, August 10, 2009

Introduction to Design Patterns as a Best Practice

This is a training I provided to other developers on my team on how to improve code quality and testability by implementing “Gang of Four” design patterns.  I plan to give another talk soon on basic TDD techniques using these same design patterns in conjunction with NUnit and Rhino Mocks.

Download Presentation and Source Code:

  • PDF version of my presentation
  • Visual Studio 2008 solution containing sample code and unit tests.

Saturday, August 1, 2009

Dynamic MapResourceManager Part 2: Initialization Using Code

This is part 2 of a 2-part post on how I set up a dynamically-configurable ESRI Web ADF MapResourceManager control. Part 1 is how I used a custom web.config section to store the ResourceItem properties for the MapResourceManager.  Part 2 shows how to dynamically initialize a MapResourceManager using the ResourceItem properties I set up in Part 1.  This approach allows for dynamic “on the fly” switching of layers displayed in an ESRI Web ADF Map control.  This methodology also provides robust layer error handling so that if an individual layer cannot be displayed (e.g., its map server is down), the remaining Map layers are displayed without crashing the entire Web ADF application.

ESRI classes generally can’t be mocked using Rhino Mocks, so the first step was to create adapters for the MapResourceItem and MapResourceManager classes, adapting only those methods needed for the initialization process, shown here:

  1:     public interface IMapResourceItemAdapter
  2:     {
  3:         bool FailedToInitialize { get; }
  4:         void InitializeResource();
  5:         string Name { get; }
  6:     }
  7:     
  8:     public class MapResourceItemAdapter : IMapResourceItemAdapter
  9:     {
 10:         private readonly MapResourceItem _mri;
 11:         public MapResourceItemAdapter(MapResourceItem mri)
 12:         {
 13:             if (mri == null)
 14:                 throw new ArgumentNullException("mri");
 15:             _mri = mri;
 16:         }
 17:         public MapResourceItem GetMapResourceItem()
 18:         {
 19:             return _mri;
 20:         }
 21:         public string Name
 22:         {
 23:             get { return _mri.Name; }
 24:         }
 25:         public void InitializeResource()
 26:         {
 27:             try
 28:             {
 29:                 _mri.InitializeResource();
 30:             }
 31:             catch { throw; }
 32:         }
 33:        public bool FailedToInitialize
 34:         {
 35:             get 
 36:             {
 37:                 try
 38:                 {
 39:                     return CalculateFailedToInitialize(_mri.FailedToInitialize, _mri.InitializationFailure, _mri.LayerDefinitions);
 40:                 }
 41:                 catch { return false; }
 42:             }
 43:         }
 44: 	// use separate method for this test because Rhino Mocks can't mock an ESRI MapResourceItem
 45:         internal static bool CalculateFailedToInitialize(bool failedToInitialize,
 46:             Exception initializationFailure, LayerDefinitionCollection layerDefinitions)
 47:         {
 48:             return (failedToInitialize || initializationFailure != null || layerDefinitions == null);
 49:         }
 50:     }
 51:     
 52:     public interface IMapResourceManagerAdapter
 53:     {
 54:         int Add(IMapResourceItemAdapter adapter);
 55:         int IndexOf(string name);
 56:         bool InitializeMapResourceManager();
 57:         void Insert(int index, IMapResourceItemAdapter adapter);
 58:         void Remove(IMapResourceItemAdapter adapter);
 59:         void RemoveAt(int index);
 60:     }
 61: 
 62:     public class MapResourceManagerAdapter : IMapResourceManagerAdapter
 63:     {
 64:         private MapResourceManager _mrm;
 65:         public MapResourceManagerAdapter(MapResourceManager mrm)
 66:         {
 67:             if (mrm == null)
 68:                 throw new ArgumentNullException("mrm");
 69:             _mrm = mrm;
 70:         }
 71:         public int Add(IMapResourceItemAdapter adapter)
 72:         {
 73:             try
 74:             {
 75:                 MapResourceItem item = ((MapResourceItemAdapter)adapter).GetMapResourceItem();
 76:                 return _mrm.ResourceItems.Add(item);
 77:             }
 78:             catch { throw; }
 79:         }
 80:         public void Remove(IMapResourceItemAdapter adapter)
 81:         {
 82:             try
 83:             {
 84:                 MapResourceItem item = ((MapResourceItemAdapter)adapter).GetMapResourceItem();
 85:                 _mrm.ResourceItems.Remove(item);
 86:             }
 87:             catch { throw; }
 88:         }
 89:         public void Insert(int index, IMapResourceItemAdapter adapter)
 90:         {
 91:             try
 92:             {
 93:                 MapResourceItem item = ((MapResourceItemAdapter)adapter).GetMapResourceItem();
 94:                 _mrm.ResourceItems.Insert(index, item);
 95:             }
 96:             catch { throw; }
 97:         }
 98:         public void RemoveAt(int index)
 99:         {
100:             try
101:             {
102:                 _mrm.ResourceItems.RemoveAt(index);
103:             }
104:             catch { throw; }
105:         }
106:         public int IndexOf(string name)
107:         {
108:             try
109:             {
110:                 return _mrm.ResourceItems.IndexOf(_mrm.ResourceItems.Find(name));
111:             }
112:             catch { throw; }
113:         }
114:         public bool InitializeMapResourceManager()
115:         {
116:             bool initialized = true;
117:             try
118:             {
119:                 List<MapResourceItem> items = new List<MapResourceItem>();
120:                 foreach (MapResourceItem mri in _mrm.ResourceItems)
121:                     items.Add(mri);
122:                 foreach (MapResourceItem mri in items)
123:                     _mrm.ResourceItems.Remove(mri);
124:             }
125:             catch { initialized = false; }
126:             return initialized;
127:         }
128:     }


I also needed a factory class to create new MapResourceItemAdapter instances.  This approach simplifies unit testing because the factory class can be mocked easily.  Here is the factory code:



  1:     public interface IMapResourceItemFactory
  2:     {
  3:         IMapResourceItemAdapter CreateMapResourceItem(TNC.GIS.WebControlsLibrary.CustomConfig.LayerProperties layerProp);
  4:     }
  5: 
  6:     public class MapResourceItemFactory : IMapResourceItemFactory
  7:     {
  8:         public IMapResourceItemAdapter CreateMapResourceItem(LayerProperties layerProp)
  9:         {
 10:             MapResourceItemAdapter adapter = null;
 11:             try
 12:             {
 13:                 MapResourceItem item = new MapResourceItem();
 14:                 item.Name = layerProp.Name;
 15:                 item.DisplaySettings.Visible = layerProp.Visible;
 16:                 item.DisplaySettings.Transparency = layerProp.Transparency;
 17:                 item.DisplaySettings.ImageDescriptor.ImageFormat = layerProp.ImageFormat;
 18:                 item.DisplaySettings.ImageDescriptor.Height = layerProp.Height;
 19:                 item.DisplaySettings.ImageDescriptor.Width = layerProp.Width;
 20:                 item.DisplaySettings.ImageDescriptor.Dpi = layerProp.Dpi;
 21:                 item.DisplaySettings.ImageDescriptor.ReturnMimeData = layerProp.ReturnMimeData;
 22:                 item.DisplaySettings.ImageDescriptor.TransparentBackground = layerProp.TransparentBackground;
 23:                 item.DisplaySettings.ImageDescriptor.TransparentColor = layerProp.TransparentColor;
 24:                 item.DisplaySettings.DisplayInTableOfContents = layerProp.DisplayInTableOfContents;
 25:                 GISResourceItemDefinition defn = new GISResourceItemDefinition();
 26:                 defn.DataSourceType = layerProp.DataSourceType;
 27:                 defn.DataSourceShared = layerProp.DataSourceShared;
 28:                 defn.DataSourceDefinition = layerProp.DataSourceDefinition;
 29:                 defn.ResourceDefinition = layerProp.ResourceDefinition;
 30:                 item.Definition = defn;
 31:                 adapter = new MapResourceItemAdapter(item);
 32:             }
 33:             catch (ArgumentNullException ex)
 34:             {
 35:                 throw new ExplanationException("The MapResourceItem could not be instantiated.", ex);
 36:             }
 37:             catch (NullReferenceException ex)
 38:             {
 39:                 throw new ArgumentNullException("The provided LayerProperties instance (layerProp) is null.", ex);
 40:             }
 41:             catch (Exception ex)
 42:             {
 43:                 throw new ArgumentException("The provided LayerProperties instance is invalid.", "layerProp", ex);
 44:             }
 45:             return adapter;
 46:         }
 47:     }


The last piece of the puzzle was to set up a MapResourceManagerInitializer class with a separate Helper class to simplify unit testing of the class logic:



  1:     public class MapResourceManagerInitializer
  2:     {
  3:         private readonly IMapResourceManagerAdapter _mrm;
  4:         private readonly IMapResourceItemFactory _mriFactory;
  5:         private readonly IErrorLogger _logger;
  6:         public MapResourceManagerInitializer(IMapResourceManagerAdapter mrm, IMapResourceItemFactory mriFactory, IErrorLogger logger)
  7:         {
  8:             if (mrm == null)
  9:                 throw new ArgumentNullException("mrm");
 10:             if (mriFactory == null)
 11:                 throw new ArgumentNullException("mriFactory");
 12:             if (logger == null)
 13:                 throw new ArgumentNullException("logger");
 14:             _mrm = mrm;
 15:             _mriFactory = mriFactory;
 16:             _logger = logger;
 17:         }
 18:         public bool InitializeAllResourceItems(Collection<LayerProperties> allLayers)
 19:         {
 20:             bool initialized = _mrm.InitializeMapResourceManager();
 21:             if (initialized)
 22:             {
 23:                 foreach (LayerProperties lp in allLayers)
 24:                 {
 25:                     IMapResourceItemAdapter item = _mriFactory.CreateMapResourceItem(lp);
 26:                     if (MapResourceManagerInitializerHelper.InitializeResourceItem(_mrm, item, _logger) == false) initialized = false;
 27:                 }
 28:             }
 29:             else 
 30:                 _logger.LogStringToFile("Unable to initialize the MapResourceManager control",
 31:                     TNC.ErrorHandlerAndLogging.Logging.LogErrorLevel.Error);
 32: 
 33:             return initialized;
 34:         }
 35:         public bool InitializeSelectedResourceItems(Collection<LayerProperties> selectedLayers)
 36:         {
 37:             bool initialized = true;
 38:             foreach (LayerProperties lp in selectedLayers)
 39:             {
 40:                 IMapResourceItemAdapter item = _mriFactory.CreateMapResourceItem(lp);
 41:                 if (MapResourceManagerInitializerHelper.InsertResourceItem(_mrm, item, _logger) == false) initialized = false;
 42:             }
 43:             return initialized;
 44:         }
 45:     }
 46: 
 47:     internal static class MapResourceManagerInitializerHelper
 48:     {
 49:         internal static bool InitializeResourceItem(IMapResourceManagerAdapter mrm,
 50:             IMapResourceItemAdapter item, IErrorLogger logger)
 51:         {
 52:             bool initialized = false;
 53:             try
 54:             {
 55:                 mrm.Add(item);
 56:                 item.InitializeResource();
 57:                 if (item.FailedToInitialize)
 58:                     mrm.Remove(item);
 59:                 else
 60:                     initialized = true;
 61:             }
 62:             catch (Exception ex)
 63:             {
 64:                 logger.LogException(ex);
 65:                 try
 66:                 {
 67:                     mrm.Remove(item);
 68:                 }
 69:                 catch { }   // do nothing
 70:             }
 71:             return initialized;
 72:         }
 73:         internal static bool InsertResourceItem(IMapResourceManagerAdapter mrm,
 74:             IMapResourceItemAdapter item, IErrorLogger logger)
 75:         {
 76:             int idx = GetItemIndex(mrm, item, logger);
 77:             if (idx == -999) return false;
 78:             bool removed = RemoveResourceItem(mrm, idx, logger);
 79:             if (removed == false) return false;
 80:             bool inserted = false;
 81:             try
 82:             {
 83:                 mrm.Insert(idx, item);
 84:                 item.InitializeResource();
 85:                 if (item.FailedToInitialize)
 86:                     RemoveResourceItem(mrm, idx, logger);
 87:                 else
 88:                     inserted = true;
 89:             }
 90:             catch (Exception ex)
 91:             {
 92:                 logger.LogException(ex);
 93:                 try
 94:                 {
 95:                     mrm.RemoveAt(idx);
 96:                 }
 97:                 catch { }   // do nothing
 98:             }
 99: 
100:             return inserted;
101:         }
102:         private static int GetItemIndex(IMapResourceManagerAdapter mrm,
103:             IMapResourceItemAdapter item, IErrorLogger logger)
104:         {
105:             try
106:             {
107:                 return mrm.IndexOf(item.Name);
108:             }
109:             catch (Exception ex)
110:             {
111:                 logger.LogException(ex);
112:                 return -999;
113:             }
114:         }
115:         private static bool RemoveResourceItem(IMapResourceManagerAdapter mrm,
116:             int idx, IErrorLogger logger)
117:         {
118:             try
119:             {
120:                 mrm.RemoveAt(idx);
121:                 return true;
122:             }
123:             catch (Exception ex)
124:             {
125:                 logger.LogException(ex);
126:                 return false;
127:             }
128:         }
129:     }


All of the pieces are assembled in the Page_Init event handler on the page containing the ESRI Web ADF MapResourceManager control.  This code combines the classes explained here in Part 2 with the code described in Part 1:



  1:     protected void Page_Init(object sender, EventArgs e)
  2:     {
  3:         if (!Page.IsCallback && !Page.IsPostBack)
  4:         {
  5:             try
  6:             {
  7:                 // instantiate a MapResourceManagerInitializer which will add the MapResourceItems to the MapResourceManager control
  8:                 MapResourceManagerInitializer initializer = new MapResourceManagerInitializer(
  9:                     new MapResourceManagerAdapter(this.MapResourceManager1), 
 10:                     new MapResourceItemFactory(),
 11:                     new ErrorLogger());
 12: 
 13:                 // instantiate a LayerPropertiesFactory which will provide the MapResourceItems settings 
 14:                 //  read from the MapResourceItemSettings custom config section
 15:                 LayerPropertiesFactory factory = new LayerPropertiesFactory(new LayerPropertiesCollection());
 16: 
 17:                 Collection<LayerProperties> layers;
 18:                 if (this.MapResourceManager1.ResourceItems.Count > 0)
 19:                 {
 20:                     // if the MapResourceManager already has been initialized, only refresh the Projects layer; get the correct layer based on user role
 21:                     if (Roles.IsUserInRole("role1"))
 22:                         layers = factory.LayersByRoleAndLayerOrder("role1", 0);
 23:                     else
 24:                         layers = factory.LayersByRoleAndLayerOrder("*", 0);
 25:                     initializer.InitializeSelectedResourceItems(layers);
 26:                 }
 27:                 else
 28:                 {
 29:                     // if the MapResourceManager has not been initialized, initialize all layers; get the correct Projects layer based on user role
 30:                     if (Roles.IsUserInRole("role1"))
 31:                         layers = factory.AllLayersByRole("role1");
 32:                     else
 33:                         layers = factory.AllLayersByRole("*");
 34:                     initializer.InitializeAllResourceItems(layers);
 35:                 }
 36:             }
 37:             catch (Exception ex)
 38:             {
 39:                 // do something with the exception
 40:             }
 41:         }
 42:     }