Managing application state


Local (DCOM) connections are only supported for ArcGIS Server versions prior to 10.1.

About managing application state

One aspect of designing an application is whether to make it stateful or stateless. You can make either stateful or stateless use of a server object running in the geographic information system (GIS) server. Stateless refers to making read-only use of a server object, meaning your application does not make changes to the server object or its associated objects. Stateful refers to read-write use of a server object where your application does make changes to the server object or its related objects.
The question of state is important in server object usage because it dictates whether server objects can be shared across application sessions. If you make stateless use of server objects, they can be shared across application sessions. If you make stateful use of server objects, they cannot be shared.

GIS server state and object pooling

This aspect of stateful versus stateless use and server object sharing relates directly to the pooling model for the server object. The following programming rules apply when using server objects:
  • Client applications cannot change the properties of a pooled server object.
  • Client applications can change the properties of a non-pooled server object.
     
Pooled server objects are expected to be used in a stateless manner. As a developer, you are responsible for confirming that the state of the server object or its associated objects, has not changed when you return the object to the pool (by releasing its context via ReleaseServerContext). Each time a user or application session makes a request to create a pooled server object, it is indeterminate which running instance it gets from the pool; therefore, all instances must have the same state or applications experience inconsistent behavior.
A server object is a coarse-grained ArcObjects component that has other associated objects. The MapServer object and its associated objects are shown in the following illustration:
Non-pooled server objects can be used in a stateful manner. Since non-pooled server objects and their contexts are destroyed when you release them, you need to hold onto them for as long as the state is important to you. When you call ReleaseServerContext, or you allow the server context to go out of scope, the server object and its context are destroyed, purging any state changes you made.

Stateful versus stateless use of server objects

Methods and properties that are exposed by server object interfaces, such as IMapServer and IGeocodeServer, and server object extension (SOE) interfaces, such as INAServer, are by their nature stateless methods, for example, IMapServer.ExportMapImage, IGeocodeServer.GeocodeAddress, and INASever.Solve. These methods do not change the properties of the server object when they are called and are, therefore, safe to call on both pooled and non-pooled server objects. Changing the state of a server object typically involves making calls to get the finer-grained ArcObjects associated with a server object and making changes at that level.
Most GIS Web applications are not stateless. Typically, each user or session can have the following:
  • Current extent
  • Set of visible layers (that can be toggled on and off through the application)
  • Different graphics visible on the map as a result of query operations, such as network tracing
It is possible to write a stateful Web application that makes stateless use of server objects in the GIS server by maintaining aspects of application state, such as the extent of the map, layer visibility, and application-added graphics, using the Web application server's session state management capabilities. Such applications are called shallowly stateful.
The GIS server also supports deeply stateful applications that use the GIS server to maintain application state. Examples of deeply stateful Web applications include the following:
  • Application that starts a geodatabase edit session on behalf of a user and works with it across multiple requests in a session to support operations, such as undo or redo
  • Application that allows a user to interactively compose a map across multiple requests within a session
The following code example shows stateless use of a MapServer object. In this example, a request is made to MapServer to draw itself at its default extent.
[C#]
IServerContext pServerContext = pSOM.CreateServerContext("RedlandsMap", "MapServer");
IMapServer pMapServer = pServerContext.ServerObject as IMapServer;

IImageType it = pServerContext.CreateObject("esriCarto.ImageType")as IImageType;
it.Format = esriImageFormat.esriImageJPG;
it.ReturnType = esriImageReturnType.esriImageReturnMimeData;

IImageDisplay idisp = pServerContext.CreateObject("esriCarto.ImageDisplay")as
    IImageDisplay;
idisp.Height = 400;
idisp.Width = 500;
idisp.DeviceResolution = 150;

IImageDescription pID = pServerContext.CreateObject("esriCarto.ImageDisplay")as
    IImageDescription;
pID.Display = idisp;
pID.Type = it;

IMapServerInfo pMapServerInfo = pMapServer.GetServerInfo(pMapServer.DefaultMapName);
IMapDescription pMD = pMapServerInfo.DefaultMapDescription;
IImageResult pMI = pMapServer.ExportMapImage(pMD, pID);

// Do something with the image.

pServerContext.ReleaseContext();
[VB.NET]
Dim pServerContext As IServerContext = pSOM.CreateServerContext("RedlandsMap", "MapServer")
Dim pMapServer As IMapServer = pServerContext.ServerObject

Dim it As IImageType = pServerContext.CreateObject("esriCarto.ImageType")
it.Format = esriImageFormat.esriImageJPG
it.ReturnType = esriImageReturnType.esriImageReturnMimeData

Dim idisp As IImageDisplay = pServerContext.CreateObject("esriCarto.ImageDisplay")
idisp.Height = 400
idisp.Width = 500
idisp.DeviceResolution = 150

Dim pID As IImageDescription = pServerContext.CreateObject("esriCarto.ImageDisplay")
pID.Display = idisp
pID.Type = it

Dim pMapServerInfo As IMapServerInfo = pMapServer.GetServerInfo(pMapServer.DefaultMapName)
Dim pMD As IMapDescription = pMapServerInfo.DefaultMapDescription
Dim pMI As IImageResult = pMapServer.ExportMapImage(pMD, pID)

' Do something with the image.

pServerContext.ReleaseContext()
The following code example shows stateful use of a MapServer object. In this example, the first layer is removed from the map, then a request is made to the MapServer to draw itself at its default extent.
[C#]
IServerContext pServerContext = pSOM.CreateServerContext("RedlandsMap", "MapServer");
IMapServer pMapServer = pServerContext.ServerObject as IMapServer;

IMapServerObjects pMapServerObjs = pMapServer as IMapServerObjects;
IMap pMap = pMapServerObjs.Map(pMapServer.DefaultMapName);
pMap.DeleteLayer(pMap.get_Layer(0));
pMapServerObjs.RefreshServerObjects();

IImageType it = pServerContext.CreateObject("esriCarto.ImageType")as IImageType;
it.Format = esriImageFormat.esriImageJPG;
it.ReturnType = esriImageReturnType.esriImageReturnMimeData;

IImageDisplay idisp = pServerContext.CreateObject("esriCarto.ImageDisplay")as
    IImageDisplay;
idisp.Height = 400;
idisp.Width = 500;
idisp.DeviceResolution = 150;

IImageDescription pID = pServerContext.CreateObject("esriCarto.ImageDisplay")as
    IImageDescription;
pID.Display = idisp;
pID.Type = it;

IMapServerInfo pMapServerInfo = pMapServer.GetServerInfo(pMapServer.DefaultMapName);
IMapDescription pMD = pMapServerInfo.DefaultMapDescription;
IImageResult pMI = pMapServer.ExportMapImage(pMD, pID);

// Do something with the image.

pServerContext.ReleaseContext();
[VB.NET]
Dim pServerContext As IServerContext = pSOM.CreateServerContext("RedlandsMap", "MapServer")

Dim pMapServer As IMapServer = pServerContext.ServerObject
Dim pMapServerObjs As IMapServerObjects = pMapServer

Dim pMap As IMap = pMapServerObjs.Map(pMapServer.DefaultMapName)
pMap.DeleteLayer pMap.Layer(0)
pMapServerObjs.RefreshServerObjects()

Dim it As IImageType = pServerContext.CreateObject("esriCarto.ImageType")
it.Format = esriImageFormat.esriImageJPG
it.ReturnType = esriImageReturnType.esriImageReturnMimeData

Dim idisp As IImageDisplay = pServerContext.CreateObject("esriCarto.ImageDisplay")
idisp.Height = 400
idisp.Width = 500
idisp.DeviceResolution = 150

Dim pID As IImageDescription = pServerContext.CreateObject("esriCarto.ImageDescription")
pID.Display = idisp
pID.Type = it

Dim pMapServerInfo As IMapServerInfo = pMapServer.GetServerInfo(pMapServer.DefaultMapName)
Dim pMD As IMapDescription = pMapServerInfo.DefaultMapDescription
Dim pMI As IImageResult = pMapServer.ExportMapImage(pMD, pID)

' Do something with the image.

pServerContext.ReleaseContext()
In the first code example, no changes were made to the server object or any of its associated objects. Once the code finishes executing and the context is released, the server object is in the same state as when the application received it. In the second code example, a layer was explicitly removed from the map using the DeleteLayer method on the IMap interface. This is an example of using a fine-grained ArcObjects component call to change the state of a server object.
Typically, when making state changes to server objects, you hold on to a reference and its context for the duration of your application session. The code example releases the server context immediately after processing the request. You would not do this type of operation with a pooled server object, as subsequent use of the instance of the MapServer object reflects that the layer was removed.
The state of a server object can be changed in a number of ways. The previous code example shows making direct changes to the properties of a server object, for example, removing a layer from a map. It is also possible to change the state of a server object indirectly through other objects in the server object's context. The following illustration summarizes the ways you can change the state of a server object: 
 

Using a stateful method on a server object

A stateful method is one that modifies or changes the instance of the server object. There are many examples of stateful methods. Common examples include methods that add or remove layers from a map server object or methods that change a layer's renderer. These methods should never be called on a pooled server object unless the client application can return the object to its original state before releasing it back to the server.

Using a stateful method on an environment

Server objects run in contexts that have a number of environment settings associated with them. Some of these environments can be modified by developers. For example, the geometry environment can be manipulated through the IGeometryEnvironment interface. While changes to the geometry environment do not directly affect a server object, those changes can affect other operations that a client application might perform using a server object's context.
The following code example shows how to change the state of a server object's environment (in this case, the geometry environment) without directly changing the server object:
[C#]
IServerContext pServerContext = pSOM.CreateServerContext("RedlandsMap", "MapServer");

IGeometryEnvironment4 pGeomEnv = pServerContext.CreateObject(
    "esriGeometry.GeometryEnvironment")as IGeometryEnvironment4;
pGeomEnv.DeviationAutoDensifyTolerance = 5.7;
pGeomEnv.DicingEnabled = true;

// Perform a geometry operation.

pServerContext.ReleaseContext();
[VB.NET]
Dim pServerContext As IServerContext = pSOM.CreateServerContext("RedlandsMap", "MapServer")

Dim pGeomEnv As IGeometryEnvironment4 = pServerContext.CreateObject("esriGeometry.GeometryEnvironment")

pGeomEnv.DeviationAutoDensifyTolerance = 5.7
pGeomEnv.DicingEnabled = True

' Perform a geometry operation.

pServerContext.ReleaseContext()
Changing the state of an environment is valid for both pooled and non-pooled server object use. To ensure that such changes to environments do not negatively impact operations made by client applications, applications should not rely on the environment being in a particular state before performing that operation. When performing operations that rely on a particular state of the environment, applications should set the required environment state before performing that operation, especially when using pooled server objects.

Working with cursors

Some objects that are created in a server context can lock or use resources that the object frees only in its destructor. For example, a geodatabase cursor can acquire a shared schema lock on a file-based feature class or table on which it is based, or can hold on to an ArcSDE stream.
While the shared schema lock is in place, other applications can continue to query or update the rows in the table, but they cannot delete the feature class or modify its schema. In the case of file-based data sources, such as shapefiles, update cursors acquire an exclusive write lock on the file that prevents other applications from accessing the file for read or write purposes. The effect of these locks is that the data might be unavailable to other applications until all the references on the cursor object are released.
In the case of ArcSDE data sources, the cursor holds on to an ArcSDE stream, and if the application has multiple clients, each can obtain and hold on to an ArcSDE stream, eventually exhausting the maximum allowable streams. The effect of the number of ArcSDE streams exceeding the maximum is that other clients fail to open their cursors to query the database.
Garbage collection is the process by which .NET reclaims memory from objects that are created by applications. Garbage collection occurs based on memory allocations being made. Garbage collection occurs when objects that are not referenced are cleaned up, which can be some time after they are out of the scope of your application.
Because of the these reasons, it is important to ensure that any reference to a cursor your application opens is released in a timely manner. If you are developing an application using .NET, the reference on the cursor (or any other Component Object Model [COM] object) is not released until garbage collection occurs. In a Web application or Web service that services multiple concurrent sessions and requests, relying on garbage collection to release references on objects results in cursors and their resources not being released in a timely manner.
When working with the Web Application Developer Framework (ADF) Web controls, the controls explicitly manage and release any COM object acquired from the server context. However, for applications that do not use the Web controls (for example, an application Web service) to ensure a COM object is released when it goes out of scope, the ESRI.ArcGIS.ADF assembly contains a helper object called ComReleaser. Use the ManageLifetime method to add a COM object to the set of objects that are explicitly released when ComReleaser is disposed. You must scope the use of ComReleaser within a using block. When you scope the use of ComReleaser within a using block, any object (including your cursor) that you have added to ComReleaser using the ManageLifetime method will be explicitly released at the end of the using block.
The following code example shows this coding pattern:
[C#]
using(ComReleaser comReleaser = new ComReleaser())
{
    IServerContext pServerContext = pSOM.CreateServerContext("RedlandsMap", 
        "MapServer");
    comReleaser.ManageLifetime(pServerContext);
    IMapServer pMapServer = pServerContext.ServerObject as IMapServer;
    comReleaser.ManageLifetime(pMapServer);

    IMapServerObjects pMapServerObjs = pMapServer as IMapServerObjects;
    comReleaser.ManageLifetime(pMapServerObjs);
    IMap pMap = pMapServerObjs.Map(pMapServer.DefaultMapName);
    comReleaser.ManageLifetime(pMap);
    IFeatureLayer flayer = pMap.get_Layer(0)as IFeatureLayer;
    comReleaser.ManageLifetime(flayer);

    IFeatureClass featureClass = flayer.FeatureClass;
    comReleaser.ManageLifetime(featureClass);

    IFeatureCursor fCursor = featureClass.Search(null, true);
    comReleaser.ManageLifetime(fCursor);

    // Do something with the cursor.

    pServerContext.ReleaseContext();
}
[VB.NET]
Dim pcomReleaser As ComReleaser = New ComReleaser()
Dim pServerContext As IServerContext = pSOM.CreateServerContext("RedlandsMap", "MapServer")
Try
pcomReleaser.ManageLifetime(pServerContext)
Dim pMapServer As IMapServer = pServerContext.ServerObject
pcomReleaser.ManageLifetime(pMapServer)

Dim pMapServerObjs As IMapServerObjects = pMapServer
pcomReleaser.ManageLifetime(pMapServerObjs)
Dim pMap As IMap = pMapServerObjs.Map(pMapServer.DefaultMapName)
pcomReleaser.ManageLifetime(pMap)
Dim flayer As IFeatureLayer = pMap.Layer(0)
pcomReleaser.ManageLifetime(flayer)

Dim featureClass As IFeatureClass = flayer.FeatureClass
pcomReleaser.ManageLifetime(featureClass)

Dim fCursor As IFeatureCursor = featureClass.Search(Nothing, True)
pcomReleaser.ManageLifetime(fCursor)

' Do something with the cursor.

Finally
pServerContext.ReleaseContext()
pcomReleaser.Dispose()
End Try

Managing state in the Web application's session state (shallowly stateful applications)

As previously mentioned, it is possible to write a stateful Web application that makes stateless use of server objects in the GIS server by maintaining aspects of application state, such as the extent of the map, layer visibility, and application-added graphics, using the Web application server's session state management capabilities. This concept is shown in the following illustration:
The IServerContext interface has methods that allow you to save GIS objects in session state by exporting them to strings. The server context also has methods to rehydrate the objects from strings as you need them. Objects that support the IPersistStream interface can be exported and rehydrated in this manner. The Web ADF Map control uses this technique to manage the user's or session's current extent as each user pans and zooms around the map.
The basic pattern the Map control uses to archive this is saving a copy of the MapServer MapDescription object in session state, and serializing and deserializing it into the server context on each draw. For example, when you zoom in by a fixed amount, the following pattern is used:
  1. Load the serialized map description to get the current map description for the session.
  2. Shrink the extent for the map description.
  3. Draw the map using the modified map description.
  4. Export the modified map description to a string so that the session state is updated.
The objects and interfaces used for managing image displays reside in the Carto object library.
The MapServer MapDescription object supports such programming patterns by allowing state to be managed in a Web server and applied to the map in a stateless manner as part of the ExportMapImage method.

Managing state in the GIS server (deeply stateful applications)

The GIS server also supports deeply stateful applications that use the GIS server to maintain application state. Supporting such applications requires a server object instance dedicated to each application session. You can configure this by making your server object non-pooled. The fact that server objects necessary for such applications are non-pooled, limits the number of concurrent sessions by the processing resources of the server.
When programming a deeply stateful Web application, use the same server context and server object throughout the session. Get a server context at the beginning of the session and hold on to it until the session has ended.
The following code example shows how to obtain a non-pooled server context and add it to your session state in an ASP.NET application:
[C#]
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        // Confirm this is a new session.
        if (Session.IsNewSession)
        {
            // Connect to the server.
            string m_host = "localhost";

            ESRI.ArcGIS.ADF.Identity agsID = new ESRI.ArcGIS.ADF.Identity("user", 
                "passwd", "domain");
            ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection conn = new
                ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection(m_host, agsID,
                true);
            IServerObjectManager som = conn.ServerObjectManager;

            IServerContext ctx = som.CreateServerContext("Farms", "MapServer");

            // Save the server context as a session variable called "context."
            Session["context"] = ctx;
        }
    }
}
[VB.NET]
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
If (Not Page.IsPostBack) Then
    ' Confirm this is a new session.
    If Session.IsNewSession Then
        ' Connect to the server.
        Dim m_host As String = "localhost"
        Dim agsID As New ESRI.ArcGIS.ADF.Identity("user", "passwd", "domain")
        Dim conn As New ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection(m_host, agsID, True)
        Dim som As IServerObjectManager = conn.ServerObjectManager
        Dim ctx As IServerContext = som.CreateServerContext("Farms", "MapServer")
        ' Save the server context as a session variable called "context."
        Session("context") = ctx
    End If
End If
End Sub
  • The following code example shows how to make use of the server context, which alters the MapServer by removing the first layer:
[C#]
protected void btnDoSomething_Click(object sender, EventArgs e)
{
    IServerContext ctx = Session["context"] as IServerContext;
    IMapServer map = ctx.ServerObject as IMapServer;

    IMapServerObjects mapObj = map as IMapServerObjects;
    IMap fgmap = mapObj.get_Map(map.DefaultMapName);

    fgmap.DeleteLayer(fgmap.get_Layer(0));
    mapObj.RefreshServerObjects();
}
[VB.NET]
Protected Sub btnDoSomething_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim ctx As IServerContext = TryCast(Session("context"), IServerContext)
Dim map As IMapServer = TryCast(ctx.ServerObject, IMapServer)
Dim mapObj As IMapServerObjects = TryCast(map, IMapServerObjects)
Dim fgmap As IMap = mapObj.get_Map(map.DefaultMapName)
fgmap.DeleteLayer(fgmap.get_Layer(0))
mapObj.RefreshServerObjects()
End Sub
  • The server context is held on to for the duration of the session and needs to be released at the end of the session. The following code example shows how to release the context when the session ends:
[C#]
protected void Session_End(Object sender, EventArgs e)
{
    IServerContext ctx = Session["context"] as IServerContext;
    ctx.ReleaseContext();
}
[VB.NET]
Protected Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)
Dim ctx As IServerContext = TryCast(Session("context"), IServerContext)
ctx.ReleaseContext()
End Sub
The session ends based on a time-out that is set in the application. In this example, the session time-out was set to five minutes, meaning if the user does not interact with the running Web application session for five minutes, the session times out, and the server context is released by the code shown previously. It also means that once a user has ended the session by closing the Web browser, the server context will not be released for five minutes until the session time-out is triggered and the code is executed.
These code examples are illustrations of how to work with server objects when building applications. It is important to note that if you are using the Web ADF controls to build a Web application, the Web controls take care of many of these details. Specifically, the ArcGIS Server Local data source, managed by MapResourceManager, takes care of releasing the server context. In addition, the Web ADF is a shallowly stateful application environment and maintains state by managing MapDescription as a native .NET value object during a user session. 
The relationship between Web controls and these aspects of the ArcGIS for Server application programming interfaces (APIs) are described in more detail in the ArcGIS for Server Help system for .NET under the following nodes in the table of contents (TOC):
  • Creating ArcGIS for Server solutions, Developing Web applications using the Web ADF, Working with the Common Data Source API, Working with data sources, ArcGIS for Server implementation in the Web ADF

Application state and scalability

The question of stateful versus stateless use of the GIS server is central to the scalability of your application. An application is more scalable than another application if it can support a larger number of users with the same amount of computer resources. The keys to scalability are as follows:
  • Make stateless use of the GIS server.
  • Use pooled server objects.
  • Minimize the time your application holds a server object. Release server objects as soon as possible and do not rely on .NET garbage collection to do it for you.
Using this criteria, it is clear that stateless or shallowly stateful applications can use object pooling and therefore, are more scalable than deeply stateful applications. The question of stateful versus stateless use of the GIS server is critical in designing your application.