ArcGIS Add Dynamic Data
ArcGIS_AddDynamicData_CSharp\App_Code\ServerObjectStateModifier.cs
// Copyright 2009 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

public class ServerObjectStateModifier
{
    // Custom enumeration for specifying the behavior of ApplySimpleRenderers
    public enum RendererAction { ApplyNew, ApplyOriginal, ApplyLast }

    // Adds the canada shapefile to the passed-in resource and assigns it the passed-in name
    public void AddLayer(ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal mapResourceLocal, string layerName)
    {
        // Get the map underlying the passed-in resource
        ESRI.ArcGIS.Carto.IMapServerObjects mapServerObjects = 
            (ESRI.ArcGIS.Carto.IMapServerObjects)mapResourceLocal.MapServer;        
        ESRI.ArcGIS.Carto.IMap aoMap = mapServerObjects.get_Map(mapResourceLocal.DataFrame);

        // Check whether the map already contains a layer with the passed-in name
        bool layerAdded = false;
        for (int i = 0; i < aoMap.LayerCount; i++)
        {
            if (aoMap.get_Layer(i).Name == layerName)
            {
                layerAdded = true;
                break;
            }
        }

        // Only execute the logic to add the layer if a layer of the same name has not already been added
        if (!layerAdded)
        {
            // Create a reference to the canada shapefile as an ArcObjects feature layer
            ESRI.ArcGIS.Server.IServerContext serverContext = mapResourceLocal.ServerContextInfo.ServerContext;
            ESRI.ArcGIS.Geodatabase.IWorkspaceFactory workspaceFactory = 
                (ESRI.ArcGIS.Geodatabase.IWorkspaceFactory)serverContext.CreateObject("esriDataSourcesFile.ShapefileWorkspaceFactory");
            ESRI.ArcGIS.Geodatabase.IFeatureWorkspace featureWorkspace = workspaceFactory.OpenFromFile(
                @"C:\temp\data\", 0) as ESRI.ArcGIS.Geodatabase.IFeatureWorkspace;
            ESRI.ArcGIS.Carto.IFeatureLayer aoFeatureLayer = (ESRI.ArcGIS.Carto.IFeatureLayer)
                serverContext.CreateObject("esriCarto.FeatureLayer");
            aoFeatureLayer.FeatureClass = featureWorkspace.OpenFeatureClass("canada.shp");
            aoFeatureLayer.Name = layerName;

            // Apply a renderer to the feature layer
            ESRI.ArcGIS.Carto.IGeoFeatureLayer aoGeoFeatureLayer = aoFeatureLayer as ESRI.ArcGIS.Carto.IGeoFeatureLayer;
            ApplySimpleRenderer(aoGeoFeatureLayer, serverContext, 0, 0, 210);

            // Set the dynamic layer's visibility based on the session variable.  Default to visible if the session variable
            // does not yet exist.
            if ((System.Web.HttpContext.Current.Session["dynamicLayerVisible"] != null))
                aoFeatureLayer.Visible = (bool)System.Web.HttpContext.Current.Session["dynamicLayerVisible"];
            else
                aoFeatureLayer.Visible = true;

            // Add the layer to the map and move it below any other layers
            aoMap.AddLayer(aoFeatureLayer);
            aoMap.MoveLayer(aoFeatureLayer, aoMap.LayerCount - 1);

            // Before pushing the changes to the server, create a dictionary containing the current visibility of the map layers.
            // We do this because updating the objects on the server (via RefreshServerObjects) also updates the map description
            // referenced by the map resource with the service's updated default description, which includes default layer 
            // visibility.
            System.Collections.Generic.Dictionary<string, bool> visibleLayers =
                new System.Collections.Generic.Dictionary<string, bool>();
            foreach (ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription layerDescription in
            mapResourceLocal.MapDescription.LayerDescriptions)
            {
                visibleLayers.Add(layerDescription.LayerID.ToString(), layerDescription.Visible);
            }

            // Register changes to server object - dynamic layer added.
            mapResourceLocal.RefreshServerObjects();

            // Reset the visibility of the layers in the resource's map description to what it was before updating the map service.
            foreach (System.Collections.Generic.KeyValuePair<string, bool> layerVisiblePair in visibleLayers)
            {
                ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality.UpdateVisibleLayer(
                    mapResourceLocal.MapDescription, layerVisiblePair.Key, layerVisiblePair.Value);
            }
        }
    }

    // Adds the passed-in GeoFeatureLayer to the passed-in local map resource and sets its position to that specified by the
    // passed-in argument
    public void AddLayer(ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal mapResourceLocal,
        ESRI.ArcGIS.Carto.IGeoFeatureLayer geoFeatureLayer, int layerIndex)
    {
        // Get the map underlying the passed-in resource
        ESRI.ArcGIS.Carto.IMapServerObjects mapServerObjects =
            (ESRI.ArcGIS.Carto.IMapServerObjects)mapResourceLocal.MapServer;
        ESRI.ArcGIS.Carto.IMap aoMap = mapServerObjects.get_Map(mapResourceLocal.DataFrame);

        // Get the server context of the passed-in map service
        ESRI.ArcGIS.Server.IServerContext serverContext = mapResourceLocal.ServerContextInfo.ServerContext;

        // Add the passed-in layer to the map and move it to the passed-in index
        aoMap.AddLayer(geoFeatureLayer);
        aoMap.MoveLayer(geoFeatureLayer, layerIndex);

        // Before pushing the changes to the server, create a dictionary containing the current visibility of the map layers.
        // We do this because updating the objects on the server (via RefreshServerObjects) also updates the map description
        // referenced by the map resource with the service's updated default description, which includes default layer 
        // visibility.  So when RefreshServerObjects is called, the visibility of the map layers will be reset to default
        System.Collections.Generic.Dictionary<string, bool> visibleLayers =
            new System.Collections.Generic.Dictionary<string, bool>();
        foreach (ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription layerDescription in
        mapResourceLocal.MapDescription.LayerDescriptions)
        {
            visibleLayers.Add(layerDescription.LayerID.ToString(), layerDescription.Visible);
        }

        // Register changes to server object, adding the passed-in layer at the map service level.
        mapResourceLocal.RefreshServerObjects();

        // Reset the visibility of the layers in the resource's map description to what it was before updating the map service.
        foreach (System.Collections.Generic.KeyValuePair<string, bool> layerVisiblePair in visibleLayers)
        {
            ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality.UpdateVisibleLayer(
                mapResourceLocal.MapDescription, layerVisiblePair.Key, layerVisiblePair.Value);
        }
    }

    // Removes the layer with the passed-in name from the passed-in resource
    public System.Collections.Generic.Dictionary<int, ESRI.ArcGIS.Carto.ILayer> RemoveLayer(
        ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal mapResourceLocal, string layerName)
    {
        // Get the map underlying the passed-in map resource
        ESRI.ArcGIS.Carto.IMapServerObjects mapServerObjects = (ESRI.ArcGIS.Carto.IMapServerObjects)mapResourceLocal.MapServer;
        ESRI.ArcGIS.Carto.IMap aoMap = mapServerObjects.get_Map(mapResourceLocal.DataFrame);

        // Get a reference to the layer with the passed-in name from the map
        ESRI.ArcGIS.Carto.IEnumLayer enumLayer = aoMap.get_Layers(null, true);
        ESRI.ArcGIS.Carto.ILayer currentLayer = null;
        System.Collections.Generic.Dictionary<int, ESRI.ArcGIS.Carto.ILayer> removedLayerDictionary =
            new System.Collections.Generic.Dictionary<int, ESRI.ArcGIS.Carto.ILayer>();
        int layerIndex = 0;
        while ((currentLayer = enumLayer.Next()) != null)
        {
            if (currentLayer.Name == layerName)
            {
                removedLayerDictionary.Add(layerIndex, currentLayer);
                break;
            }

            layerIndex++;
        }

        // Make sure the layer was found before executing logic to remove it
        if (currentLayer != null)
        {
            // Delete the layer from the map
            aoMap.DeleteLayer(currentLayer);

            // Before committing the change to the map service, create a dictionary containing the current visibility of the map layers.
            // We do this because updating the objects on the server (via RefreshServerObjects) also updates the map description
            // referenced by the map resource with the service's updated default description, which includes default layer visibility.
            System.Collections.Generic.Dictionary<string, bool> visibleLayers = new System.Collections.Generic.Dictionary<string, bool>();
            foreach (ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription layerDescription in
                mapResourceLocal.MapDescription.LayerDescriptions)
            {
                visibleLayers.Add(layerDescription.LayerID.ToString(), layerDescription.Visible);
            }

            // Register the layer removal with the server object
            mapResourceLocal.RefreshServerObjects();

            // Reset the visibility of the remaining layers back to what it was before the update
            foreach (System.Collections.Generic.KeyValuePair<string, bool> layerVisiblePair in visibleLayers)
            {
                ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality.UpdateVisibleLayer(
                    mapResourceLocal.MapDescription, layerVisiblePair.Key, layerVisiblePair.Value);
            }
        }

        return removedLayerDictionary;
    }

    // Moves the layer with the passed-in name to the passed-in index within the passed-in resource
    public int MoveLayer(ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal mapResourceLocal, 
        string layerName, int layerIndex)
    {
        // Get the map underlying the passed-in map resource
        ESRI.ArcGIS.Carto.IMapServerObjects mapServerObjects = 
            (ESRI.ArcGIS.Carto.IMapServerObjects)mapResourceLocal.MapServer;
        ESRI.ArcGIS.Carto.IMap aoMap = mapServerObjects.get_Map(mapResourceLocal.DataFrame);

        if ((layerIndex < 0) || (layerIndex > aoMap.LayerCount - 1))
            return -1;

        // Get a reference to the layer with the passed-in name from the map
        ESRI.ArcGIS.Carto.IEnumLayer enumLayer = aoMap.get_Layers(null, true);
        ESRI.ArcGIS.Carto.ILayer currentLayer = null;
        while ((currentLayer = enumLayer.Next()) != null)
        {
            if (currentLayer.Name == layerName)
                break;
        }

        // Make sure the layer was found before executing logic to move it
        if (currentLayer != null)
        {
            // Move the layer to the passed-in layer index
            aoMap.MoveLayer(currentLayer, layerIndex);

            // Before committing the change to the map service, create a dictionary containing the current visibility of the map layers.
            // We do this because updating the objects on the server (via RefreshServerObjects) also updates the map description
            // referenced by the map resource with the service's updated default description, which includes default layer visibility.
            System.Collections.Generic.Dictionary<string, bool> visibleLayers = new System.Collections.Generic.Dictionary<string, bool>();
            foreach (ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription layerDescription in
                mapResourceLocal.MapDescription.LayerDescriptions)
            {
                visibleLayers.Add(layerDescription.LayerID.ToString(), layerDescription.Visible);
            }

            // Register the layer repositioning with the server object
            mapResourceLocal.RefreshServerObjects();

            // Reset the visibility of the remaining layers back to what it was before the update
            foreach (System.Collections.Generic.KeyValuePair<string, bool> layerVisiblePair in visibleLayers)
            {
                ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality.UpdateVisibleLayer(
                    mapResourceLocal.MapDescription, layerVisiblePair.Key, layerVisiblePair.Value);
            }
        }

        return layerIndex;
    }

    // Changes the renderers for all layers belonging to the passed-in local map resource.  If rendererAction is ApplyNew, 
    // the renderers are set to simple renderers with randomly generated colors.  If ApplyOriginal, the renderers are set 
    // to what they were when the page first loaded.  If ApplyLast, then the renderers are set to the last new renderer
    // applied.
    public void ApplySimpleRenderers(ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceLocal mapResourceLocal, 
        RendererAction rendererAction)
    {
        // Get a reference to the ArcObjects map and layers via server context
        ESRI.ArcGIS.Server.IServerContext aoServerContext = mapResourceLocal.ServerContextInfo.ServerContext;
        ESRI.ArcGIS.Carto.IMapServerObjects aoMapServerObjects =
            (ESRI.ArcGIS.Carto.IMapServerObjects)mapResourceLocal.MapServer;
        ESRI.ArcGIS.Carto.IMap aoMap = aoMapServerObjects.get_Map(mapResourceLocal.DataFrame);
        ESRI.ArcGIS.Carto.IEnumLayer enumLayer = aoMap.get_Layers(null, true);

        // For each layer, we will store a dictionary containing its initial renderers (i.e. on initial load) in session.
        // Check whether the session variable for the current layer has already been created.  If so, retrieve it.  If not, 
        // create a new dictionary.  This dictionary will be used to reset renderers to their initial load state. 
        System.Collections.Generic.Dictionary<int, ESRI.ArcGIS.Carto.IFeatureRenderer> originalRendererDictionary = null;
        string originalRendererKey = string.Format("{0}_originalRendererDictionary", mapResourceLocal.Name);
        if (System.Web.HttpContext.Current.Session[originalRendererKey] == null)
            originalRendererDictionary = new System.Collections.Generic.Dictionary<int, ESRI.ArcGIS.Carto.IFeatureRenderer>();
        else
            originalRendererDictionary = (System.Collections.Generic.Dictionary<int, ESRI.ArcGIS.Carto.IFeatureRenderer>)
                System.Web.HttpContext.Current.Session[originalRendererKey];

        // For each layer, we will also store a dictionary containing its current renderers.  This will be used to re-apply 
        // the current renderer on each page request.
        System.Collections.Generic.Dictionary<int, ESRI.ArcGIS.Carto.IFeatureRenderer> currentRendererDictionary = null;
        string currentRendererKey = string.Format("{0}_currentRendererDictionary", mapResourceLocal.Name);
        if (System.Web.HttpContext.Current.Session[currentRendererKey] == null)
            currentRendererDictionary = new System.Collections.Generic.Dictionary<int, ESRI.ArcGIS.Carto.IFeatureRenderer>();
        else
            currentRendererDictionary = (System.Collections.Generic.Dictionary<int, ESRI.ArcGIS.Carto.IFeatureRenderer>)
                System.Web.HttpContext.Current.Session[currentRendererKey];

        // Loop through each layer in the current map, either applying a new random simple renderer, reverting to the random simple
        // renderer that was last applied, or reverting the renderer to its initial load state.
        int layerIndex = 0;
        ESRI.ArcGIS.Carto.IGeoFeatureLayer aoGeoFeatureLayer = null;
        while ((aoGeoFeatureLayer = (ESRI.ArcGIS.Carto.IGeoFeatureLayer)enumLayer.Next()) != null)
        {
            switch (rendererAction)
            {
                case RendererAction.ApplyNew:
                    // If the current layer's renderer is not in the dictionary of initial renderers, then this is the
                    // first time this code has been executed during the session.  The layer's current renderer is 
                    // therefore the initial load state renderer, so we add it to the original renderer dictionary.
                    if (!originalRendererDictionary.ContainsKey(layerIndex))
                    {
                        originalRendererDictionary.Add(layerIndex, aoGeoFeatureLayer.Renderer);
                        System.Web.HttpContext.Current.Session[originalRendererKey] = originalRendererDictionary;
                    }

                    // Apply a randomly colored simple renderer to the current layer
                    ApplySimpleRenderer(aoGeoFeatureLayer, aoServerContext, -1, -1, -1);

                    // If the current renderer dictionary contains the current layer's renderer, remove it so it can
                    // be replaced with the new renderer.
                    if (currentRendererDictionary.ContainsKey(layerIndex))
                        currentRendererDictionary.Remove(layerIndex);

                    // Update the current renderer dictionary with the current renderer
                    currentRendererDictionary.Add(layerIndex, aoGeoFeatureLayer.Renderer);
                    System.Web.HttpContext.Current.Session[currentRendererKey] = currentRendererDictionary;
                    break;
                case RendererAction.ApplyLast:
                    // Set the layer's renderer to the last random simple renderer applied
                    aoGeoFeatureLayer.Renderer = (ESRI.ArcGIS.Carto.IFeatureRenderer)currentRendererDictionary[layerIndex];
                    break;
                case RendererAction.ApplyOriginal:
                    // Set the layer's renderer to the one it had when the session started
                    if (originalRendererDictionary.ContainsKey(layerIndex))
                        aoGeoFeatureLayer.Renderer = (ESRI.ArcGIS.Carto.IFeatureRenderer)originalRendererDictionary[layerIndex];
                    break;
            }

            layerIndex++;
        }

        // Get the current resource's map description before the map and layers are updated on the server.  We do this
        // because when the server objects are updated, the layer visibility reverts to that specified by the map 
        // document underlying the map service, and the map description contains information about current layer 
        // visibility
        ESRI.ArcGIS.ADF.ArcGISServer.MapDescription adfMapDescription = mapResourceLocal.MapDescription;
        
        // Register the layer changes with the server.  This applies the new renderer.
        mapResourceLocal.RefreshServerObjects();

        // Set the resource's map description to that retrieved before server objects were updated, so layer visibility
        // is the same as before the update.
        mapResourceLocal.MapDescription = adfMapDescription;
    }

    // Generates a simple renderer and applies it to the passed-in layer.  For any passed-in color value that is not within the
    // valid range (0-255), a random value within the valid range is applied.  So, for example, a randomly colored renderer can 
    // be generated by passing in RGB parameters of -1, -1, and -1.
    private void ApplySimpleRenderer(ESRI.ArcGIS.Carto.IGeoFeatureLayer aoGeoFeatureLayer,
        ESRI.ArcGIS.Server.IServerContext aoServerContext, int red, int green, int blue)
    {
        // Generate an ArcObjects color for the renderer.  Use the specified RGB value if valid, otherwise use a random integer within
        // the valid range.
        ESRI.ArcGIS.Display.IRgbColor aoRgbColor = (ESRI.ArcGIS.Display.IRgbColor)aoServerContext.CreateObject("esriDisplay.RgbColor");
        System.Random randomizer = new System.Random();
        if ((red < 0) || (red > 255))
            aoRgbColor.Red = randomizer.Next(0, 256);
        else
            aoRgbColor.Red = red;

        if ((green < 0) || (green > 255))
            aoRgbColor.Green = randomizer.Next(0, 256);
        else
            aoRgbColor.Green = green;

        if ((blue < 0) || (blue > 255))
            aoRgbColor.Blue = randomizer.Next(0, 256);
        else
            aoRgbColor.Blue = blue;

        // Instantiate an ArcObjects simple renderer to use in specifying the layer's renderer
        ESRI.ArcGIS.Carto.ISimpleRenderer aoSimpleRenderer = (ESRI.ArcGIS.Carto.ISimpleRenderer)
            aoServerContext.CreateObject("esriCarto.SimpleRenderer");

        // Check the layer's geometry type and create a symbol for the renderer accordingly
        ESRI.ArcGIS.Geometry.esriGeometryType geometryType = aoGeoFeatureLayer.FeatureClass.ShapeType;
        switch (aoGeoFeatureLayer.FeatureClass.ShapeType)
        {
            case ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPoint:
                ESRI.ArcGIS.Display.ISimpleMarkerSymbol aoSimpleMarkerSymbol =
                    (ESRI.ArcGIS.Display.ISimpleMarkerSymbol)aoServerContext.CreateObject("esriDisplay.SimpleMarkerSymbol");
                aoSimpleMarkerSymbol.Color = (ESRI.ArcGIS.Display.IColor)aoRgbColor;
                aoSimpleRenderer.Symbol = (ESRI.ArcGIS.Display.ISymbol)aoSimpleMarkerSymbol;
                break;
            case ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolyline:
                ESRI.ArcGIS.Display.ISimpleLineSymbol aoSimpleLineSymbol =
                    (ESRI.ArcGIS.Display.ISimpleLineSymbol)aoServerContext.CreateObject("esriDisplay.SimpleLineSymbol");
                aoSimpleLineSymbol.Color = (ESRI.ArcGIS.Display.IColor)aoRgbColor;
                aoSimpleRenderer.Symbol = (ESRI.ArcGIS.Display.ISymbol)aoSimpleLineSymbol;
                break;
            case ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon:
                ESRI.ArcGIS.Display.ISimpleFillSymbol aoSimpleFillSymbol =
                    (ESRI.ArcGIS.Display.ISimpleFillSymbol)aoServerContext.CreateObject("esriDisplay.SimpleFillSymbol");
                aoSimpleFillSymbol.Color = (ESRI.ArcGIS.Display.IColor)aoRgbColor;
                aoSimpleRenderer.Symbol = (ESRI.ArcGIS.Display.ISymbol)aoSimpleFillSymbol;
                break;
            default:
                throw new System.Exception("No renderer or symbol selected.  Shape type undetermined.");
        }

        // Assign the new renderer to the passed-in layer
        aoGeoFeatureLayer.Renderer = (ESRI.ArcGIS.Carto.IFeatureRenderer)aoSimpleRenderer;
    }
}