Wednesday, March 16, 2011

File GeoDatabase: Getting the Workspace

I have created FileWorkspaceUtils that inherits from WorkspaceUtils, it adds the functions GetRows and GetFeatures that return the raw IRow and IFeature data. In WorkspaceUtils I preferred that the low level programmer won’t even know he has something called IRow or IFeature.

  1. public class FileWorkspaceUtils:WorkspaceUtils
  2. {
  3.     public FileWorkspaceUtils(IFeatureWorkspace workspace) : base(workspace)
  4.     {
  5.     }
  6.  
  7.     public List<IRow> GetRows(string tableName)
  8.     {
  9.         var result = new List<IRow>();
  10.         DoActionOnSelectRows(tableName, null, row => result.Add(row.Clone()));
  11.         return result;
  12.     }
  13.  
  14.     public List<IFeature> GetFeatures(string layerName)
  15.     {
  16.         var result = new List<IFeature>();
  17.         DoActionOnSelectFeatures(layerName, null, feature => result.Add(feature.Clone()));
  18.         return result;
  19.     }
  20. }

//TODO: Post on the wonder of Extension Methods (row.Clone())

I have added code to WorkspaceProvider so that it will return the FileWorkspaceUtils (independent of File/Personal GeoDatabase):

  1. private const string PersonalGeoDatabaseFileExtension = ".MDB";
  2. private const string FileGeoDatabaseFileExtension = ".GDB";
  3.  
  4. /// <summary>
  5. /// Get a File WorkspaceUtils for Personal and File GeoDatabase
  6. /// </summary>
  7. /// <param name="filePath"></param>
  8. /// <returns></returns>
  9. public FileWorkspaceUtils GetFileWorkspace(string filePath)
  10. {
  11.     var extension = (Path.GetExtension(filePath) ?? String.Empty).ToUpper();
  12.     if (extension.CompareTo(PersonalGeoDatabaseFileExtension) == 0)
  13.         return CreatePersonalGeoDatabaseWorkspace(filePath);
  14.     if (extension.CompareTo(FileGeoDatabaseFileExtension) == 0)
  15.         return CreateFileGeoDatabaseWorkspace(filePath);
  16.  
  17.     throw new NotImplementedException("The only supported file types are mdb and gdb. Not: " + extension);
  18. }
  19.  
  20. private FileWorkspaceUtils CreatePersonalGeoDatabaseWorkspace(string filePath)
  21. {
  22.     AccessWorkspaceFactory workspaceFactory = new AccessWorkspaceFactoryClass();
  23.  
  24.     var workspace = workspaceFactory.OpenFromFile(filePath, 0);
  25.     return new FileWorkspaceUtils((IFeatureWorkspace)workspace);
  26. }
  27.  
  28. private FileWorkspaceUtils CreateFileGeoDatabaseWorkspace(string filePath)
  29. {
  30.     FileGDBWorkspaceFactory workspaceFactory = new FileGDBWorkspaceFactoryClass();
  31.  
  32.     var workspace = workspaceFactory.OpenFromFile(filePath, 0);
  33.     return new FileWorkspaceUtils((IFeatureWorkspace)workspace);
  34. }

The only problem is it doesn’t work, my unit tests that just check GetFileWorkspace throws a COMException:

Test method CompanyName.GIS.Core.Esri.Tests.WorkspaceProviderTests.GetWorkspace_ValidPersonalGeoDB_GetFileWorkspaceUtils threw exception:
System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x80040228
at ESRI.ArcGIS.DataSourcesGDB.AccessWorkspaceFactoryClass.OpenFromFile(String fileName, Int32 hWnd)
at Core.Esri.WorkspaceProvider.CreatePersonalGeoDatabaseWorkspace(String filePath) in WorkspaceProvider.cs: line 200
at Core.Esri.WorkspaceProvider.GetFileWorkspace(String filePath) in WorkspaceProvider.cs: line 189
at Core.Esri.Tests.WorkspaceProviderTests.GetWorkspace_ValidPersonalGeoDB_GetFileWorkspaceUtils() in WorkspaceProviderTests.cs: line 55

The problem was caused by Licensing, I changed EsriInitilization to contained the old style licensing as well (the one with IAoInitialize, the new stuff is using RuntimeManager):

All my unit tests (427 tests) pass, so it works…

  1. public class EsriInitilization
  2. {
  3.     private static bool _isStarted = false;
  4.  
  5.     public static void Start()
  6.     {
  7.         if (_isStarted)
  8.             return;
  9.  
  10.         if (!Initialize(ProductCode.Server, esriLicenseProductCode.esriLicenseProductCodeArcServer))
  11.         {
  12.             if(!Initialize(ProductCode.Engine, esriLicenseProductCode.esriLicenseProductCodeEngineGeoDB))
  13.             {
  14.                 throw new ApplicationException(
  15.                     "Unable to bind to ArcGIS license Server nor to Engine. Please check your licenses.");
  16.             }
  17.         }
  18.         _isStarted = true;
  19.     }
  20.  
  21.     private static bool Initialize(ProductCode product, esriLicenseProductCode esriLicenseProduct)
  22.     {
  23.         if (RuntimeManager.Bind(product))
  24.         {
  25.             IAoInitialize aoInit = new AoInitializeClass();
  26.             aoInit.Initialize(esriLicenseProduct);
  27.             return true;
  28.         }
  29.         return false;
  30.     }
  31. }

That still throw an exception, this time simply because IFeature refused to be cloned – though it implemented ESRI’s IClone interface. The error I got was:

//TODO: Write error and new code

//TODO: Post after writing about Extension Method (TODO above)

Resources:

Esri Forum: COM Exception 0x80040228 When Opening a Personal Geodatabase

 

Keywords: License, COM, exception, IWorkspace, engine, Server, ArcGis, ESRI, Unit tests, MDB, GDB