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 IMapResourceItemAdapter2: {3: bool FailedToInitialize { get; }4: void InitializeResource();5: string Name { get; }6: }7:8: public class MapResourceItemAdapter : IMapResourceItemAdapter9: {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 Name22: {23: get { return _mri.Name; }24: }25: public void InitializeResource()26: {27: try28: {29: _mri.InitializeResource();30: }31: catch { throw; }32: }33: public bool FailedToInitialize34: {35: get36: {37: try38: {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 MapResourceItem45: 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 IMapResourceManagerAdapter53: {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 : IMapResourceManagerAdapter63: {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: try74: {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: try83: {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: try92: {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: try101: {102: _mrm.ResourceItems.RemoveAt(index);103: }104: catch { throw; }105: }106: public int IndexOf(string name)107: {108: try109: {110: return _mrm.ResourceItems.IndexOf(_mrm.ResourceItems.Find(name));111: }112: catch { throw; }113: }114: public bool InitializeMapResourceManager()115: {116: bool initialized = true;117: try118: {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 IMapResourceItemFactory2: {3: IMapResourceItemAdapter CreateMapResourceItem(TNC.GIS.WebControlsLibrary.CustomConfig.LayerProperties layerProp);4: }5:6: public class MapResourceItemFactory : IMapResourceItemFactory7: {8: public IMapResourceItemAdapter CreateMapResourceItem(LayerProperties layerProp)9: {10: MapResourceItemAdapter adapter = null;11: try12: {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 MapResourceManagerInitializer2: {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: else30: _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 MapResourceManagerInitializerHelper48: {49: internal static bool InitializeResourceItem(IMapResourceManagerAdapter mrm,50: IMapResourceItemAdapter item, IErrorLogger logger)51: {52: bool initialized = false;53: try54: {55: mrm.Add(item);56: item.InitializeResource();57: if (item.FailedToInitialize)58: mrm.Remove(item);59: else60: initialized = true;61: }62: catch (Exception ex)63: {64: logger.LogException(ex);65: try66: {67: mrm.Remove(item);68: }69: catch { } // do nothing70: }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: try82: {83: mrm.Insert(idx, item);84: item.InitializeResource();85: if (item.FailedToInitialize)86: RemoveResourceItem(mrm, idx, logger);87: else88: inserted = true;89: }90: catch (Exception ex)91: {92: logger.LogException(ex);93: try94: {95: mrm.RemoveAt(idx);96: }97: catch { } // do nothing98: }99:100: return inserted;101: }102: private static int GetItemIndex(IMapResourceManagerAdapter mrm,103: IMapResourceItemAdapter item, IErrorLogger logger)104: {105: try106: {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: try119: {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: try6: {7: // instantiate a MapResourceManagerInitializer which will add the MapResourceItems to the MapResourceManager control8: 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 settings14: // read from the MapResourceItemSettings custom config section15: 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 role21: if (Roles.IsUserInRole("role1"))22: layers = factory.LayersByRoleAndLayerOrder("role1", 0);23: else24: layers = factory.LayersByRoleAndLayerOrder("*", 0);25: initializer.InitializeSelectedResourceItems(layers);26: }27: else28: {29: // if the MapResourceManager has not been initialized, initialize all layers; get the correct Projects layer based on user role30: if (Roles.IsUserInRole("role1"))31: layers = factory.AllLayersByRole("role1");32: else33: layers = factory.AllLayersByRole("*");34: initializer.InitializeAllResourceItems(layers);35: }36: }37: catch (Exception ex)38: {39: // do something with the exception40: }41: }42: }
No comments:
Post a Comment