Joining an external table to a geo-feature layer

1788
3
10-10-2012 11:50 AM
GeorgeFaraj
Occasional Contributor III
I'm trying to join an external table to a IGeoFeatureLayer, and it works well. The problem is when I save this map and then reload the .mxd, the FeatureClass of the layer is null, and the Table of Contents shows a warning symbol next to the layer. Is it failing to serialize/deserialize the joined feature class? What is the correct approach to solve this problem?

Here's the code I'm using:

  string externalFields = GetExternalFields(joinFields);

  ITable table = GetExternalTable(externalFields);

  geoFeatureLayer.FeatureClass = CreateFeatureClass(table, externalFields);

// ...

  private ITable GetExternalTable(string externalFields)
  {
   IWorkspaceFactory2 workspaceFactory2;
   IWorkspace workspace;
   ISqlWorkspace sqlWorkspace;
   ITable table;

   using (var comReleaser = new ComReleaser())
   {
    var query = string.Format(
     QUERY_BASE, externalFields, synchronizer.LayerManager.ParcelLayer.GetParcelYear()
    );
    var factoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.SqlWorkspaceFactory");

    workspaceFactory2 = Activator.CreateInstance(factoryType) as IWorkspaceFactory2;
    workspace = workspaceFactory2.OpenFromString(this.connection, 0);
    sqlWorkspace = workspace as ISqlWorkspace;

    comReleaser.ManageLifetime(workspaceFactory2);
    comReleaser.ManageLifetime(sqlWorkspace);

    var queryDescription = sqlWorkspace.GetQueryDescription(query);
    queryDescription.OIDFields = EXTERNAL_KEY;

    var datasetName = "S";
    sqlWorkspace.CheckDatasetName("S", queryDescription, out datasetName);

    table = sqlWorkspace.OpenQueryClass(datasetName, queryDescription);
   }

   return table;
  }

  private IFeatureClass CreateFeatureClass(ITable externalTable, string externalFields)
  {
   IGeoFeatureLayer geoFeatureLayer = synchronizer.LayerManager.ParcelLayer as IGeoFeatureLayer;
   IMemoryRelationshipClassFactory memoryRelationshipFactory = null;
   IRelQueryTableFactory relQueryTableFactory = null;
   IRelQueryTable relQueryTable = null;

   try
   {
    var memoryRelationshipClassFactory = Type.GetTypeFromProgID("esriGeodatabase.MemoryRelationshipClassFactory");
    memoryRelationshipFactory = Activator.CreateInstance(memoryRelationshipClassFactory) as IMemoryRelationshipClassFactory;

    var relationshipClass = memoryRelationshipFactory.Open(
     "Join",
     geoFeatureLayer.DisplayFeatureClass as IObjectClass,
     FEATURE_KEY, externalTable as IObjectClass, EXTERNAL_KEY,
     "forward", "backward", esriRelCardinality.esriRelCardinalityOneToOne
    );

    var relQueryTableFactoryType = Type.GetTypeFromProgID("esriGeodatabase.RelQueryTableFactory");
    relQueryTableFactory = Activator.CreateInstance(relQueryTableFactoryType) as IRelQueryTableFactory;

    relQueryTable = relQueryTableFactory.Open(
     relationshipClass, true, null, null, externalFields, true, true
    ) as IRelQueryTable;
   }
   finally
   {
    Marshal.FinalReleaseComObject(memoryRelationshipFactory);
    Marshal.FinalReleaseComObject(relQueryTableFactory);
   }

   return relQueryTable as IFeatureClass;
  }
 }
0 Kudos
3 Replies
RichWawrzonek
Occasional Contributor
The IMemoryRelationshipClassFactory object you create is not an actual feature class but an in memory join that points to the source datasets. Since you are pulling data from an SDE the .mxd can not find the source. You can manually save the layer by right-clicking it and choosing Data > Export Data and then save as a feature class on disk. Or write some code to export it to disk.
0 Kudos
GeorgeFaraj
Occasional Contributor III
The IMemoryRelationshipClassFactory object you create is not an actual feature class but an in memory join that points to the source datasets. Since you are pulling data from an SDE the .mxd can not find the source. You can manually save the layer by right-clicking it and choosing Data > Export Data and then save as a feature class on disk. Or write some code to export it to disk.


I'm doing this in my own application, not on ArcMap, so manually exporting it through that menu is not an option. Can you point me in the right direction to export the joined feature class to disk using the ArcObjects API?

Thanks!
0 Kudos
RichWawrzonek
Occasional Contributor
The geoprocessor object is a bit slow but lets you do this by writing very little code. Try passing your joined feature class into the example below. The alternative is to roll your own export routine that would copy the data line by line to the necessary format. The more general it needs to be the more code you need to write, but isn't that always the case 🙂

private bool CreateFC(IFeatureClass featureClass)
        {

            //THIS CREATES THE AN EMPTY FEATURE CLASS WITH ALL NEEDED FIELDS IN THE TEMP FWS
            IFeatureWorkspace TempFWS = CreateFWS(); //Create the feature workspace that you need, lots of examples online
            if(TempFWS == null) return false;
            TempFWS.CreateFeatureClass("YourFcName", featureClass.Fields, null, null, esriFeatureType.esriFTSimple, featureClass.ShapeFieldName, "");

            //THIS POPULATES THE FC WITH ALL THE FEATURES
            Geoprocessor gp = new Geoprocessor(); //starts a GEOprocess.
            gp.SetEnvironmentValue("workspace", "<path + geodb name>"); //sets the geodb to use (ex. C:\GIS\temp.mdb)
            gp.AddOutputsToMap = false;
            ESRI.ArcGIS.DataManagementTools.Append append = new ESRI.ArcGIS.DataManagementTools.Append(); //this tool uploads the feature class
            append.inputs = featureClass;
            append.target = "Path + geodb name + layer name";
                // example: System.IO.Path.GetTempPath() + @"\" + temp.mdb + @"\" + LayerName;
            try
            {
                gp.Execute(append, null);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                return false;
            }

            return true;
        }
0 Kudos