Select to view content in your preferred language

File Geodatabase API 1.1 .NET Wrapper: ObjectDisposedException

3766
3
08-22-2011 08:37 AM
Jan-Tschada
Esri Contributor
As far as I can judge it the .NET Wrapper is implemented using C++/CLI. This means that each "managed class" has a finalizer (e.g. Table::!Table) and from a C# perspective this class implements IDisposable. The finalizer is mainly used for releasing unmanaged resources. Taking a look at the core API the "valid state" of a Table is manged by using Geodatabase::OpenTable and Geodatabase::CloseTable. This means the managed Table instance must have a reference to the corresponding Geodatabase instance. I believe that the finalizer Table::!Table always tries to use this Geodatabase instance. This can lead to an ObjectDisposedException when the Geodatabase instance was already disposed by the developer or by the "Garbage Collector" (brain bug).

I can reproduce this by using the follwing code snippet:
internal static void DisposeGeodatabase(string path, string tableName)
{
 Geodatabase geodatabase = Geodatabase.Open(path);
 try
 {
  Table table = geodatabase.OpenTable(tableName);
  try
  {
   Console.WriteLine(table.RowCount);
  }
  finally
  {
   table.Close();
   // Not disposing the table leads to an ObjectDisposedException in the finalizer (Table::!Table())
   //table.Dispose();
  }
 }
 finally
 {
  geodatabase.Close();
  // The geodatabase must not be disposed!
  geodatabase.Dispose();
 }
}


If this is true, any developer have to take care about this fact. The easiest way could be a combination of the using directive and a kind of visitor implementation. Like the following snippet shows:
private static void Apply(string path, Action<Geodatabase> strategy)
{
 using (Geodatabase geodatabase = Geodatabase.Open(path))
 {
  try
  {
   strategy(geodatabase);
  }
  finally
  {
   geodatabase.Close();
  }
 }
}


I put some effort in this kind of workaround and designed some "helper classes" which can be combined using lambdas. The following snippet shows how to iterate through all rows of a table. You can modify the RowCollectionStrategy to obtain a specific subset of the table.

internal static void IterateAllRows(string path, string tableName)
{
 GeodatabaseStrategy geodatabaseStrategy = new GeodatabaseStrategy(path);
 geodatabaseStrategy.Apply((geodatabase) =>
 {
  TableStrategy tableStrategy = new TableStrategy(geodatabase, tableName);
  tableStrategy.Apply((table) =>
  {
   long count = DetermineAccessibleRows(table);
   Console.WriteLine("{0} rows of table '{1}' processed", count, tableName);
  });
 });
}

private static long DetermineAccessibleRows(Table table)
{
 RowCollectionStrategy rowCollectionStrategy = RowCollectionStrategy.CreateRecycleAll(table);
 long count = 0;
 rowCollectionStrategy.Apply((rows) =>
 {
  foreach (Row row in rows)
  {
   count++;
  }
 });
 return count;
}


Would you be so kind and try to investigate if this unexpected "Dispose behaviour" is correct.

This prototype workaround implementation (all the sample data is not included) can be obtained by:
SourceForge
Product Manager
Developers and Location Services
Germany and Switzerland
0 Kudos
3 Replies
DanielWalton
Occasional Contributor
J.T.,

Thank you for your efforts on this. I have seen the same issue myself, in addition to several other instabilities that probably relate the wrapper implementation. I will try your helper classes out.
Thanks again!
0 Kudos
CharlesTilly
New Contributor III
What is the latest on this?  I see the problem is still around for the most recent version of the API.  Any word from ESRI on how to resolve?
0 Kudos
SubbuA
by
New Contributor
Hi,
I am not able to get workaround implementation for this issue(C#) from SourceForge. so can you help me to download the sample API for this.

Regards
Subbu
0 Kudos