How to create workspace extensions


This document was published with and applies to ArcGIS 9.3.
A 10 version also exists. A 9.2 version also exists.
Summary A workspace extension extends the functionality of a geodatabase in a manner that applies to the whole database rather than individual datasets. For this reason, workspace extensions are used less often than class extensions as a way of extending geodatabase behavior.

This topic shows how to implement a workspace extension using Visual Studio 2005 and the .NET Framework. It explains the advantages and disadvantages associated with the different methods of deployment for a workspace extension and how to deploy an extension.

The required interfaces implemented by workspace extensions are discussed, as are event handlers, which can be used with workspace extensions. Code examples show a simple extension that implements the required interfaces and optional interface. The extension shown has minimal functionality, but provides a starting point for application specific functionality to be added.

Development licensing Deployment licensing
ArcView ArcView
ArcEditor ArcEditor
ArcInfo ArcInfo

To use the code in this topic, reference the following namespaces via the using (C#) or Imports (VB .NET) statements. Add the corresponding references to the project to gain access to these application programming interfaces (APIs):

In this topic


Common uses for workspace extensions

Use workspace extensions to customize geodatabase behavior that applies to the entire geodatabase, rather than individual datasets. The following are examples of functionality that can be implemented with workspace extensions:
 
If the geodatabase is only being accessed by ArcGIS Desktop clients, investigate implementing an editor extension as an alternative to the geodatabase extension, as it may be more appropriate for some of these tasks (that is, listening to edit events).

Geodatabase registration vs. component category registration

A workspace extension can be registered in two different ways, which results in significantly different behavior. Whether geodatabase registration or component category registration is appropriate depends on the purpose of the extension, and should be considered prior to the extension's design and implementation. It is recommended that an extension be designed for geodatabase or component category registration (not both).
 
Registering a workspace extension with a geodatabase adds the name and globally unique identifier (GUID) of the extension to the GDB_Extensions geodatabase system table. When a client connects to the geodatabase, the extension will be initialized; if the client does not have the extension, it will be unable to use the geodatabase. The advantage of this method guarantees any client connecting to the geodatabase behaves in a similar manner. For example, if the workspace extension has logging functionality, the logs include every client that has used or is using the geodatabase. Only a single workspace extension can be registered with a geodatabase and only a geodatabase administrator can register an extension with a geodatabase. A significant caveat of using this type of extension is if it unexpectedly fails, the data can be unavailable from any client.
 
Using component category registration with a workspace extension adds the extension to the client's ESRI Geodatabase Workspace Extensions component category. Rather than being associated with a specific geodatabase, the extension will be initialized and used whenever the client connects to any geodatabase, be it personal, file, or ArcSDE. The decision to use component category registration with a workspace extension should be carefully considered before implementation, as in most cases it is not required. One advantage that this type of registration offers is that a geodatabase can have multiple workspace extensions operating simultaneously.
 
The following table shows the advantages and disadvantages of geodatabase and component category registration:
 
 
Geodatabase registration
Component category registration
Advantages
  • Behavior is present for every client.
  • Multiple workspace extensions can operate simultaneously.
  • In the event of extension failure, data can be reached from another client.
  • Extensions can be registered without administrative privileges.
Disadvantages
  • Geodatabase and extensions have, at most, a 1:1 relationship.
  • In the event of extension failure, the data can be unreachable from all clients.
  • Administrative privileges are required to register an extension with the geodatabase.
  • Clients are not required to have the extension and may not implement the desired behavior.
 
Viewing a geodatabase's extensions
Use the IWorkspaceExtensionManager interface to obtain a list of a workspace's extensions.  The following code example shows how to do this:
 

[C#]
public void ListWorkspaceExtensions(IWorkspace workspace)
{
  // Cast the workspace to the IWorkspaceExtensionManager and loop through 
  // its extensions.
  IWorkspaceExtensionManager extensionManager = (IWorkspaceExtensionManager)
    workspace;
  for (int i = 0; i < extensionManager.ExtensionCount; i++)
  {
    IWorkspaceExtension extension = extensionManager.get_Extension(i);
    UID extensionUID = extension.GUID;
    Console.WriteLine("Name: {0} - UID: {1}", extension.Name,
      extensionUID.Value);
  }
}

[VB.NET]
Public Sub ListWorkspaceExtensions(ByVal workspace As IWorkspace)
    ' Cast the workspace to the IWorkspaceExtensionManager and loop through
    ' its extensions.
    Dim extensionManager As IWorkspaceExtensionManager = CType(workspace, IWorkspaceExtensionManager)
    Dim i As Integer
    For i = 0 To extensionManager.ExtensionCount - 1
        Dim extension As IWorkspaceExtension = extensionManager.Extension(i)
        Dim extensionUID As UID = extension.GUID
        Console.WriteLine("Name: {0} - UID: {1}", extension.Name, extensionUID.Value)
    Next i
End Sub
Even with a new geodatabase, the preceding code example shows several workspace extensions, because it includes geodatabase registered and component category registered extensions (which includes the address locator extension, the representation class extension, and so on) in the list.
 
Geodatabase registration and unregistration
The easiest way to register an extension with a geodatabase is to create a small, focused application for the task. Registering a workspace extension with a geodatabase is straightforward and only has to be done once. Any client will thereafter be required to have the extension to work with the geodatabase.
 
To register an extension this way, connect to the geodatabase in the usual way (for more information, see How to connect to a geodatabase) and cast the provided IWorkspace reference to the IWorkspaceExtensionManager interface. This interface provides several properties and methods for extension registration and access. The following code example shows how to use this interface to register an extension:
 

[C#]
// For example, extensionName = "ConnectLog.ConnectLogWorkspaceExtension"
// extensionUID = "{d10a65c7-9257-4902-bd23-e666537bce01}"
public void RegisterWorkspaceExtension(IWorkspace workspace, String
  extensionName, String extensionUID)
{
  // Create a new unique identifier (UID) object for the extension.
  UID uid = new UIDClass();
  uid.Value = extensionUID;

  // Cast the workspace to the IWorkspaceExtensionManager interface.
  IWorkspaceExtensionManager extensionManager = (IWorkspaceExtensionManager)
    workspace;

  // Try to register the extension.
  try
  {
    extensionManager.RegisterExtension(extensionName, uid);
  }
  catch (COMException comExc)
  {
    // TODO: Handle the exception in a way appropriate to your application.
  }
}

[VB.NET]
' For example, extensionName = "ConnectLog.ConnectLogWorkspaceExtension"
' extensionUID = "{d10a65c7-9257-4902-bd23-e666537bce01}"

Public Sub RegisterWorkspaceExtension(ByVal workspace As IWorkspace, ByVal extensionName As String, ByVal extensionUID As String)
    ' Create a new UID object for the extension.
    Dim uid As UID = New UIDClass()
    uid.Value = extensionUID
    
    ' Cast the workspace to the IWorkspaceExtensionManager interface.
    Dim extensionManager As IWorkspaceExtensionManager = CType(workspace, IWorkspaceExtensionManager)
    
    ' Try to register the extension.
    Try
    extensionManager.RegisterExtension(extensionName, uid)
    Catch comExc As COMException
    ' TODO: Handle the exception in a way appropriate to your application.
    End Try
End Sub
Attempting to register multiple extensions with a geodatabase, results in a Component Object Model (COM) exception.
If an extension is to be unregistered—because it is not needed or it fails and makes the geodatabase unusable—use a similar method. The following code example shows how to do this:
 

[C#]
// For example, extensionUID = "{d10a65c7-9257-4902-bd23-e666537bce01}"
public void UnregisterWorkspaceExtension(IWorkspace workspace, String
  extensionUID)
{
  // Create a new UID object for the extension.
  UID uid = new UIDClass();
  uid.Value = extensionUID;

  // Cast the workspace to the IWorkspaceExtensionManager and unregister the extension.
  IWorkspaceExtensionManager extensionManager = (IWorkspaceExtensionManager)
    workspace;
  extensionManager.UnRegisterExtension(uid);
}

[VB.NET]
' For example, extensionUID = "{d10a65c7-9257-4902-bd23-e666537bce01}"

Public Sub UnregisterWorkspaceExtension(ByVal workspace As IWorkspace, ByVal extensionUID As String)
    ' Create a new UID object for the extension.
    Dim uid As UID = New UIDClass()
    UID.Value = extensionUID
    
    ' Cast the workspace to the IWorkspaceExtensionManager and unregister the extension.
    Dim extensionManager As IWorkspaceExtensionManager = CType(workspace, IWorkspaceExtensionManager)
    ExtensionManager.UnRegisterExtension(uid)
End Sub
Component category registration and unregistration
Component category registration can be implemented by including COM registration and unregistration methods that call static methods from classes in the ArcGIS .NET Application Development Framework (ADF). The following code example shows these methods:
 

[C#]
[ComRegisterFunction()][ComVisible(false)] static void RegisterFunction(Type
  registerType)
{
  string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}",
    registerType.GUID);
  GeodatabaseWorkspaceExtensions.Register(regKey);
}

[ComUnregisterFunction()][ComVisible(false)] static void UnregisterFunction
  (Type registerType)
{
  string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}",
    registerType.GUID);
  GeodatabaseWorkspaceExtensions.Unregister(regKey);
}

[VB.NET]
<ComRegisterFunction(), ComVisible(False)> _
                     Shared Sub RegisterFunction(ByVal registerType As Type)
Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
GeodatabaseWorkspaceExtensions.Register(regKey)
End Sub

<ComUnregisterFunction(), ComVisible(False)> _
                       Shared Sub UnregisterFunction(ByVal registerType As Type)
Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
GeodatabaseWorkspaceExtensions.Unregister(regKey)
End Sub
The attributes before the methods and the calls to the GeodatabaseWorkspaceExtensions class are required, but the methods can be named arbitrarily. Once these required methods are added, the extension can be registered by building the solution in Visual Studio. If a dynamic-link library (DLL) is to be distributed rather than source code, the extension can be registered by using the .NET Framework Regasm tool. For more information, see How to register .NET components with COM.
 
If a component category registered workspace extension is to be unregistered, the simplest method is to use the .NET Framework Regasm tool. The previously mentioned How to register .NET components with COM topic shows how to do this, as does the installed help for the Regasm tool.
 

Adding the required attributes

The following attributes are required (and recommended) when creating a workspace extension:
 
The following code example shows these three attributes:
 

[C#]
[Guid("d10a65c7-9257-4902-bd23-e666537bce01")][ClassInterface
  (ClassInterfaceType.None)][ProgId("ConnectLog.ConnectLogWorkspaceExtension")]
public class ConnectLogWorkspaceExtension: IWorkspaceExtension2,
  IWorkspaceExtensionControl
{
  // Class members here...
}

[VB.NET]
<Guid("d10a65c7-9257-4902-bd23-e666537bce01"), _
      ClassInterface(ClassInterfaceType.None), _
      ProgId("ConnectLog.ConnectLogWorkspaceExtension")> _
      Public Class ConnectLogWorkspaceExtension
Implements IWorkspaceExtension2, IWorkspaceExtensionControl

' Class members here...

End Class
The GUID should be a randomly generated unique value. The class interface attribute defines whether an interface is generated for the workspace extension class; passing the ClassInterfaceType.None value to the attribute (see the preceding code example) is recommended. The ProgId attribute's value should be the fully qualified name of the workspace extension; that is, the namespace followed by the class name.
 
All three attributes can be automatically generated by using the ArcGIS Visual Studio integration tools as follows:
  1. In Visual Studio 2005, open a project.
  2. Right-click the project on the Solution Explorer.
  3. Choose Add, then New Item to display the Add New Item dialog box.
  4. On the Templates pane, click ArcGIS Class, type an appropriate name in the Name text box, and click Add to display the ArcGIS Class Wizard.
  5. Choose All, then General from the Customization Group drop-down lists.
  6. Click COM Class under the Base Component area and click Finish. A new class will be added to the project, with the previous three attributes automatically generated.
 

Registering for COM interop

For a workspace extension to function properly, it should be built as part of a class library registered for COM interop. In Visual Studio 2005, do the following:
  1. Right-click the extension's project on the Solution Explorer and choose Properties.
  2. On the Properties window, click the Build tab.
  3. Select the Register for COM interop check box near the bottom of the Properties window.
 
If the Register for COM interop check box is disabled and cannot be selected, it is because the current project is not the appropriate type. Make sure it is a class library, not an application.

Implementing required and optional interfaces

At a minimum, workspace extensions should implement two interfaces—either IWorkspaceExtension or IWorkspaceExtension2, and IWorkspaceExtensionControl. IWorkspaceExtension and IWorkspaceExtension2 (and children) interfaces have properties for the extension's name and GUID, as well as properties for hiding data dictionary and private tables from users. The IWorkspaceExtensionControl interface provides methods for extension initialization and shutting down.
 
Implementing IWorkspaceExtension2, rather than IWorkspaceExtension, is recommended. The IWorkspaceExtension2 interface has a Workspace property that allows objects referencing the extension to easily find the workspace. Although this isn't required for all extensions, it can be a convenience for those that do.
In addition to these interfaces, the following optional interfaces can also be implemented:
 
Finally, new interfaces can be created and implemented by an extension as a way of caching geodatabase wide behavior and data with new methods and properties. Although ESRI clients (such as ArcCatalog) will not have access to these members, custom clients that know about the new interface or interfaces will.
It is important to remember that a workspace extension can be called by any geodatabase client, including ArcMap, ArcCatalog, and custom clients. Because of this, an extension should never be responsible for output, as both graphical user interface (GUI) output (that is, in the form of message boxes) or console output will be inappropriate for some applications. Writing to the local file system, while useful in some cases, should be implemented carefully, as not all client machines will have a common file system (for example, many users may not have a C:\Temp directory).
IWorkspaceExtensionControl
IWorkspaceExtensionControl should be the first interface implemented when creating a new workspace extension (has two methods Init and Shutdown). Init is called when the workspace extension is initialized, and has an IWorkspaceHelper parameter. From a workspace helper, the source workspace can be accessed. In most workspace extension implementations, it will be necessary to store a reference to the workspace helper in a member variable. The following code example shows how to do this:
 

[C#]
private IWorkspaceHelper workspaceHelper = null;

public void Init(IWorkspaceHelper workspaceHelper)
{
  // Set the private workspace helper field to the parameter.
  this.workspaceHelper = workspaceHelper;
}

[VB.NET]
Private workspaceHelper As IWorkspaceHelper = Nothing

Public Sub Init(ByVal workspaceHelper As IWorkspaceHelper) Implements IWorkspaceExtensionControl.Init
    ' Set the private workspace helper field to the parameter.
    Me.workspaceHelper = workspaceHelper
End Sub
It may be tempting to store a reference to the workspace in a member variable, rather than the helper. This is strongly discouraged, as it creates a circular reference and can cause problems (that is, the Shutdown method may not be called). Rather than storing the workspace in a member variable, derive it from the workspace helper when needed.
 
It is important to note that a workspace extension is not necessarily initialized when a connection to the workspace is made. Specific actions, such as requesting a list of datasets from the workspace, starting an edit session, or opening a dataset triggers the initialization. With a client such as ArcCatalog, this is not an issue, since ArcCatalog will always request a list of datasets following a connection. If an extension's functionality is dependant on it always being initialized, and custom clients are being used, test this carefully to ensure the clients are triggering the initialization.
 
The Shutdown method is also required, but with garbage collection being native to the .NET Framework, it is much less important than it is when creating a workspace extension outside the .NET Framework. The following is a simple code example for Shutdown:
 

[C#]
public void Shutdown()
{
  workspaceHelper = null;
}

[VB.NET]
Public Sub Shutdown() Implements IWorkspaceExtensionControl.Shutdown
    workspaceHelper = Nothing
End Sub
As with the Init method, the timing of when (and if at all) the Shutdown method is called must be considered. The Shutdown method is called by a workspace when it is being disposed of. If this method contains crucial functionality, any workspaces using the extension should be explicitly released using the Marshal.ReleaseComObject method, rather than leaving the garbage collection to the .NET runtime environment; this ensures Shutdown is called.
 
IWorkspaceExtension and IWorkspaceExtension2
A workspace extension is required to implement IWorkspaceExtension or IWorkspaceExtension2. There are six properties these interfaces specify, the details of which can be found by clicking on the interfaces' links. The first two properties that should be implemented are Name and GUID. The following code example shows this:
 

[C#]
public String Name
{
  get
  {
    return "Connect Log Workspace Extension";
  }
}

public UID GUID
{
  get
  {
    UID uid = new UIDClass();
    uid.Value = "{d10a65c7-9257-4902-bd23-e666537bce01}";
    return uid;
  }
}

[VB.NET]
Public ReadOnly Property Name() As String Implements IWorkspaceExtension.Name, IWorkspaceExtension2.Name
Get
Return "Connect Log Workspace Extension"
End Get
End Property

Public ReadOnly Property GUID() As UID Implements IWorkspaceExtension.GUID, IWorkspaceExtension2.GUID
Get
Dim uid As UID = New UIDClass()
uid.Value = "{d10a65c7-9257-4902-bd23-e666537bce01}"
Return uid
End Get
End Property
The DataDictionaryTableNames and PrivateDatasetNames properties are similar in purpose but should be implemented different. DataDictionaryTableNames should return an enumerator of table names that are used by the workspace extension to store application specific data; if none exists, null can be returned. PrivateDatasetNames should return an enumerator of feature datasets, feature classes, relationship classes, and other datasets that are used internally by the workspace and should not be exposed to the user.
 
The return type of these properties is IEnumBSTR. ArcObjects libraries do not provide a cocreatable class that implements this interface, leaving it the developer's responsibility to do so. The interface defines two methods, Next and Reset. A simple implementation could be little more than a wrapper around a generic list of strings and an enumerator. The following code example uses a cocreatable class, EnumBSTR, with a constructor that accepts an array of strings as a parameter. The following code example also shows how to define a table named "COUNTRY" as a data dictionary table:
 

[C#]
public IEnumBSTR DataDictionaryTableNames
{
  get
  {
    String[] hiddenTables = 
    {
      "COUNTRY"
    };
    IEnumBSTR enumBSTR = new EnumBSTR(hiddenTables);
    return enumBSTR;
  }
}

[VB.NET]
Public ReadOnly Property DataDictionaryTableNames() As IEnumBSTR Implements IWorkspaceExtension.DataDictionaryTableNames, IWorkspaceExtension2.DataDictionaryTableNames
Get
Dim hiddenTables() As String = New String() {"COUNTRY"}
Dim enumBSTR As IEnumBSTR = New EnumBSTR(hiddenTables)
Return enumBSTR
End Get
End Property
If a geodatabase does not contain the table or tables specified as data dictionary tables, no exception will be thrown. It is extremely important, however, not to request dataset names from the workspace to dynamically construct a list of data dictionary tables, as doing so adversely affects the performance of the geodatabase and its clients. If a dynamic list of data dictionary tables is required, a table containing such a list (marked as a data dictionary table) can be used and a custom interface can be defined on the workspace extension for table access and modification.
 
The IWorkspaceExtension2 interface has two additional properties, OwnsDatasetType and Workspace. OwnsDatasetType accepts an esriDatasetType value as a parameter and returns a Boolean indicating whether the extension "owns" the dataset. An example of a dataset type owned by a workspace extension is the Feature Class Representation Extension, which owns representation classes. For most workspace extensions, returning a false value from this property is appropriate. The Workspace property should return a reference to the extension's workspace. If a private field has been created for the workspace helper (for more information, see IWorkspaceExtensionControl in this topic), this can be implemented easily, as the following code example shows:
 

[C#]
public IWorkspace Workspace
{
  get
  {
    return workspaceHelper.Workspace;
  }
}

[VB.NET]
Public ReadOnly Property Workspace() As IWorkspace Implements IWorkspaceExtension2.Workspace
Get
Return workspaceHelper.Workspace
End Get
End Property
Optional interfaces
IWorkspaceEvents is an optional interface that can be implemented by workspace extensions for notification of dataset creation, deletion, and renaming. IWorkspaceEditEvents is used to handle notification related to edit sessions. IVersionEvents and IVersionEvents2 can be implemented if a workspace extension should be notified of versioning events, such as reconciliation and conflict detection. Implementing IWorkspaceReplicaEvents results in two methods—one called prior to child replica creation and one called after the replication is complete.
 
A workspace extension is not required to implement any of these, but the following explains how to implement the IWorkspaceEvents interface for the purpose of logging.
 
The first step in the process is creating the methods called by the workspace. The following code example shows a simple example of the interface's three methods, with a private field for storing a log file name:
 

[C#]
private const String logFile = @"C:\Temp\IWorkspaceEventsLog.txt";

public void OnCreateDataset(IDataset dataset)
{
  String createMessage = "Dataset created: {0} At: {1}\n";
  String output = String.Format(createMessage, dataset.Name, DateTime.Now);
  File.AppendAllText(logFile, output);
}

public void OnDeleteDataset(IDataset dataset)
{
  String deleteMessage = "Dataset deleted: {0} At: {1}\n";
  String output = String.Format(deleteMessage, dataset.Name, DateTime.Now);
  File.AppendAllText(logFile, output);
}

public void OnRenameDataset(IDataset dataset, String oldName, String newName)
{
  String renameMessage = "Dataset renamed from: {0} to: {1} At: {2}\n";
  String output = String.Format(renameMessage, oldName, newName, DateTime.Now);
  File.AppendAllText(logFile, output);
}

[VB.NET]
Private Const logFile As String = "C:\Temp\IWorkspaceEventsLog.txt"

Public Sub OnCreateDataset(ByVal dataset As IDataset) Implements IWorkspaceEvents.OnCreateDataset
    Dim createMessage As String = "Dataset created: {0} At: {1}{2}"
    Dim output As String = String.Format(createMessage, dataset.Name, DateTime.Now, vbCrLf)
    File.AppendAllText(logFile, output)
End Sub

Public Sub OnDeleteDataset(ByVal dataset As IDataset) Implements IWorkspaceEvents.OnDeleteDataset
    Dim deleteMessage As String = "Dataset deleted: {0} At: {1}{2}"
    Dim output As String = String.Format(deleteMessage, dataset.Name, DateTime.Now, vbCrLf)
    File.AppendAllText(logFile, output)
End Sub

Public Sub OnRenameDataset(ByVal dataset As IDataset, ByVal oldName As String, ByVal newName As String) Implements IWorkspaceEvents.OnRenameDataset
    Dim renameMessage As String = "Dataset renamed from: {0} to: {1} At: {2}{3}"
    Dim output As String = String.Format(renameMessage, oldName, newName, DateTime.Now, vbCrLf)
    File.AppendAllText(logFile, output)
End Sub
With the methods created, add IWorkspaceEvents to the list of interfaces implemented by the extension.
 
Event handling with a workspace extension is slightly different from other event handlers, such as custom event listeners. Rather than explicitly "wiring" event handling methods to the source of the events, workspace extensions automatically receive events by implementing their optional interfaces, such as IVersionEvents. Custom objects can also define event handlers for this interface. For more information, see How to listen to versioned events.

Summary

A workspace extension can be a useful tool for customizing a geodatabase's behavior, but as with other geodatabase customizations, it should be carefully planned and tested prior to deployment. An extension that contains serious errors or is improperly registered, can create problems for database administrators and users. Deciding whether an extension is best deployed as a component category registered extension or as a geodatabase registered extension is also very important. 
 
When implementing the IWorkspaceExtension properties or optional interfaces, particularly those for editing or versioning, scalability should be a consideration. These members can be called frequently in enterprise systems and database intensive operations could negatively affect performance.


See Also:

How to register .NET components with COM
How to register classes in COM component categories
How to listen to versioned events
How to use replica creation events to extend the replica creation process
How to connect to a geodatabase