Adding a FeatureLayer by Name

Feature layers are immensely useful and powerful types that can be used within your Esri web applications. At a basic level they can be thought of as a GraphicsLayer with an associated QueryTask. The usual way of adding a FeatureLayer would be to reference the url of the layer you want to use. This url is composed of the map service endpoint and the layer id e.g.http://server/ArcGIS/rest/services/name/MapServer/0 where 0 is the layer id.

What we will look at now is how we can add a FeatureLayer by referencing the layer name instead of the id. The reason that this would be useful is that this would protect your layer from failing to load if the underlying map document that has been published as a service has had its layer order changed as this would mean that the layer id would change too.

To achieve this functionality we can query the REST endpoint of the service, lookup the service information and then use this to determine the layer id that corresponds to the supplied layer name. This technique can be used to retrieve all of the map service information but here I will just be returning the layer id and name.

Here’s the code to create the FeatureLayer

public static class FeatureLayerExtensions
    public static void FromLayerNameEndpoint(string mapServiceLayerNameEndpoint,
                                                Action<ESRI.ArcGIS.Client.FeatureLayer> onLayerCreated)
        var featureLayer = new ESRI.ArcGIS.Client.FeatureLayer();

                                (fLayer, url) =>
                                    fLayer.Url = url;
                                    if (onLayerCreated != null) onLayerCreated(fLayer);

    public static void GetLayerId(this ESRI.ArcGIS.Client.FeatureLayer featureLayer,
                                    string endpoint,
                                    Action<ESRI.ArcGIS.Client.FeatureLayer, string> onLayerIdFound)
        featureLayer.GetLayerId(endpoint, string.Empty, onLayerIdFound);

    public static void GetLayerId(this ESRI.ArcGIS.Client.FeatureLayer featureLayer,
                                    string endpoint,
                                    string proxyUrl,
                                    Action<ESRI.ArcGIS.Client.FeatureLayer, string> onLayerIdFound)
        var layerName = endpoint.TrimEnd('/').Split('/').Last();
        var uri = endpoint.TrimEnd('/').Substring(0, endpoint.TrimEnd('/').LastIndexOf('/'));

        var request = new WebClient();
        request.OpenReadCompleted += (sender, e) =>
            // Using the built in Json serializer but you could use something else e.g. Json.NET
            var s = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(LayerInfo));
            var layers = ((LayerInfo)s.ReadObject(e.Result)).Layers;

            foreach (var layer in layers)
                if (string.Equals((string)layer.Name, layerName, StringComparison.OrdinalIgnoreCase))
                    if (onLayerIdFound != null) onLayerIdFound(featureLayer, string.Format("{0}/{1}", uri, layer.Id));

            throw new InvalidOperationException(string.Format("A layer with the name '{0}' was not found in the map service.", layerName));

        if (string.IsNullOrEmpty(proxyUrl))
            request.OpenReadAsync(new Uri(EnsureJsonResponseFormatUrl(uri)));
            request.OpenReadAsync(new Uri(string.Format("{0}?{1}", proxyUrl, EnsureJsonResponseFormatUrl(uri))));
    static string EnsureJsonResponseFormatUrl(string url)
        if (url == null) throw new ArgumentNullException("url");

        if (url.IndexOf("f=json") == -1)
            if (url.IndexOf('?') == -1)
                url += "?";
                url += "&";
            return url + "f=json";
            return url;

and the data contract that is returned from the endpoint

public class LayerInfo
    [DataMember(Name = "layers")]
    public LayerInfoItem[] Layers { get; set; }


/// <summary>
/// Contains layer properties from the MapServer REST endpoint.
/// Could be extended to return all values available e.g. type, minScale etc. 
/// </summary>
public class LayerInfoItem
    [DataMember(Name = "id")]
    public int Id { get; set; }

    [DataMember(Name = "name")]
    public string Name { get; set; }

and how to call it

var url = " from last 7 days";
                                                (featureLayer) =>
                                                    // TODO : any other initialization 
                                                    featureLayer.Opacity = 0.6;

Now when you run your application you will see the FeatureLayer added to your map. If you have fiddler running you can see the requests being made. I’d encourage you to dig into these to help understand the process involved.

A final point; A FeatureLayer cannot have its Url property set after the layer has been initialized. This is important here as it means that we need to create and add the layer in code rather than with markup.

ArcGIS Server REST SOEs with Complex Types

A week or so ago I had to write my first ArcGIS Server SOE in order to add some custom functionality into a legacy .NET web forms application, if you are not familiar with the concept of SOEs (Server Object Extensions) then I suggest you start by reading the Esri help as there is a fair amount of good documentation on the ArcGIS resource pages

All the examples were geared around using simple types as operation parameters which in some cases is fine but I decided to build mine using complex types. There are a few reasons that I prefer this approach.

  • If I need to add a parameter that is related to the type I can just add it as a property rather than having to edit the operation definition.
  • If I want to work with large amounts of data or collections of data then having to manually add these can be very laborious.
  • Having to manually parse the operation input to extract parameters values is also tedious.
  • I wanted to simplify the calling of the SOE from managed code and make the underlying mechanism as transparent to the user as possible.

Sample Implementation

For the purposes of this post I’ve created a sample solution that contains the basic structure for how I implemented this. The solution consists of

  • The SOE (which is a self registering console application – thanks to Kirk Kuykendall for the idea on this)
  • A common dll used to define the SOE model and names
  • A client dll used as an API to invoke the SOE operations
  • A client test project, in this case just a web page that calls the extension

Click here to download the source

Note that you will need the ArcGIS .NET SDK installed to build the solution.

Working with Complex Types as input parameters

There are standard conventions that must be followed to build a SOE so that it can be used with ArcGIS Server. The method definition for a SOE operation is

private byte[] SampleOperationHandler(NameValueCollection boundVariables,  
                                      JsonObject operationInput,  
                                      string outputFormat,  
                                      string requestProperties,  
                                  out string responseProperties)

the input parameter values are part of the operationInput and each one must be looked up by it’s name as defined in the operation schema. This schema is defined in the CreateRestSchema method and for the sample it looks like

private RestResource CreateRestSchema() 
    var soeResource = new RestResource(_soeName, false, RootResHandler); 

    // Add the operations 
    var operations = new List<RestOperation>(); 

    var sampleRestOperation = new RestOperation( 
                new[] { typeof(GpsData).Name }, 
                new[] { "json" }, 

    // TODO : add more operations as needed 
    soeResource.operations = operations; 

    return soeResource; 

where GpsData is the name of the only parameter I’m defining. The type of this parameter isn’t defined so in theory I could pass anything but we need to know what it is when we parse the operationInput in order to extract the correct data. If we are using the Simple Type approach then it may be something like operationInput.TryGetAsBoolean… but here we are passing a Json object so we can use

JsonObject jsonObj;  
bool found = operationInput.TryGetJsonObject(typeof(GpsData).Name, out jsonObj); 
if (!found) 
    throw new ArgumentNullException(typeof(GpsData).Name); 
var gpsData = JsonableObject.FromJson<GpsData>(jsonObj.ToJson());

Now we have our deserialised object containing our data to work with in a few lines rather than the same code per property.  The conversion to and from Json is handled using the Json .NET library.

Invoking the Extension

Once the SOE is built and registered we need a way of calling it. The ClientTest project is a standard ASP.NET web site with one page that calls the sample operation on a button click.

protected void btnCallSampleOperation_Click(object sender, EventArgs e) 
    var gpsData = new GpsData 
        LayerName = "Land area", 
        NumberOfSatellites = 4, 
        FixAcquired = true, 
        Location = new Location 
            Longitude = 175.495605, 
            Latitude = -39.546977 

   var result = OperationHelper.CallSampleOperation( 

Note that we only have to specify the url of the MapServer endpoint as the helper method constructs the full url for us. This provides a very clean and easy way to not only invoke the extension but also to ensure that we aren’t going to get runtime errors due to using magic strings.

public static OperationResult CallSampleOperation(  
                   string restEndpoint,  
                   GpsData gpsData,  
                   string proxyUrl) 
   var fullUrl = string.Format("{0}/exts/{1}/{2}", 
   var parameters = new Dictionary<string, string> 
         { { typeof(GpsData).Name, gpsData.ToJson() } }; 
   var request = new Request(fullUrl, proxyUrl); 
   return request.SubmitRequest<OperationResult>(parameters); 

Checking the Data

The best way to find out how the SOE works is to run it for yourself. First you need to publish a map service with at least one layer and enable the extension.

soe in arccatalog

Once you have it setup you can browse the REST endpoint for your service and there will be an extra option showing the Extension

rest metadata 1

Click on that to view the supported operations. This page can be customised, here I have included the version number for the SOE.


If you click on the SampleOperation you will see the following screen which can also be used to test the extension so long as you input the data correctly.

sampple operation

An easier way is to run the test page though and view the request using Fiddler. If you start the TestClient Default.aspx page and click on the button you should see the following


This shows the Json as part of the message body with the result below. Note that you will have to change the values for the layer name and REST service url to match your environment.

Hopefully this gives you a starting point for working with REST SOEs and complex types in your applications. Any feedback is appreciated and thanks for reading.