Object Oriented Design & ArcGIS Server DCOM: Incompatible?

One of the big promises of ArcGIS Server is that you can build web services that access ArcObjects. This opens up all kinds of interesting options. But some of the requirements of using those (DCOM) ArcObjects cause problems in object-oriented designs.
 
To illustrate the problem, I’ll talk about a web service that I’m currently working on.
 
Map Series Web Service
The concept is quite simple: create a web service that will generate 1:100K PDF maps, based on the selection of a 1:100K tile. The front end will be an ArcIMS powered portal, and since the PDF production takes a few minutes, the resultant map will be emailed to the user. All layout etc is handled in the MXD – the only thing the web service does is swap out some text for titles, and zoom to the correct tile. This is about the simplest “real-world” web service.
 
Since I like to keep my code organized, I created some business objects to actually do the work behind the scenes (this also makes it much more testable, but that’s another post)…
 
SheetService Class
This class actually handles the request – the asmx just spins up an instance of this class and let’s it do the work. It reads needed info from a config file, creates the other helper classes (below), connects to a SOC, locates the tile feature, zooms the map, and creates the PDF and shoots off the email.
 
MapTitle Class
This parses map title information from an XmlNode passed to it by SheetService as it is reading web.config. It is responsible for setting the text of the title elements in the layout, and then re-setting them back to their original “tags” so they can be located the next time the context is used.
 
TileLayer Class
This class is involved with holding settings from the config file which specify information about the tile layer itself – the layername, the dataframe it is contained in, the field to be used for the Tile Title.
 
TileLocator Class
This class handles drawing the active 1:100K tile onto the locator map. It also parses config info from a node passed to it by SheetService as it is reading web.config. Similar to the MapTitle, after the PDF is generated, this class needs to delete the tile element so that it will not be on the map the next time the context is used.
 
DCOM & ServerContext
The hitch is that with ArcGIS Server, whenever you explicity create a new object, you must do it in the SOC – via context.CreateObject(“esriWhatever.SomeClass”).  This way the actual object is created in the SOC, and you get back a proxy which communicates back via DCOM. Thus any class that uses ArcObjects also needs access to IServerContext. In order to make sure that these objects will be disposed of correctly, you also need to call WebObject.ManageLifetime once you have a pointer to the proxy. The same goes for objects you create implicitly (i.e. by casting). Thus you also need access to a WebObject.
 
The question is: How to we manage the lifetimes of ArcObjects (and their proxies) in our custom business objects?
 
Solutions?
One option is to pass along both the Server Context and web object into any methods. Passing in a webobject instance is particularly thorny, since it is typically created via a using () block, anything you add into it will be cleaned up when it is disposed. Thus you must be very careful about accessing properties which may expose ArcObjects (proxied) for fear that the webObject disposed, and thus you get null back. This sort of thing could get really really ugly to debug.
 
The next option is to pass in a ServerContext only, and create a webObject as needed, in it’s own using block. This makes for cleaner method signatures, but now you are ensuring that you dispose all of your created ArcObjects when the method completes, but what if you need to hold onto some of those objects for longer than your method call? Next!
 
Much of the example code, and lots of code in the forums seems to go with the “Big-Ugly-Function” design philosophy. Simply stuff everything you need to do in one large function, wrap that in a using ( WebObject webObj = new WebObject(true) ) block, call CreateObject and ManageLifetime every other line, and you’re off. Just because this is a common pattern does not change the fact that it’s evil. Re-use? Nah. Unit Testing? Good Luck! Next!
 
The other option I can come up with is to write all our AGS logic as COM objects that run in the SOC. This is somewhat compelling (and the recommended pattern for Cross-Product Development), as you can do away with all the CreateObject and ManageLifetime stuff, but it would seem that you then have other problems in relating those classes into whatever you are doing on the “client” side of the DCOM connection. And what of the case when only part of your object model is involved in the geospatial aspect of things?
 
What are you doing?
For this project, I’ll likely go with wrapping the classes into COM, but I’m very interested in what other people are doing to address this issue. I doubt that I’m the only developer who wants to be able to have a nice clean object model to work with, and DCOM certainly adds another layer of complexity.

Advertisement
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s