C # и ArcMaps: Как передать входные данные пользовательского интерфейса в фоновый поток, не получая SERVERFAULT? - PullRequest
0 голосов
/ 04 декабря 2018

Я помогаю коллеге отладить инструмент, который он создает для ArcGis.Он используется для создания реплик и выполняется путем выбора различных входных данных из выпадающих списков в winform.

Проблема в том, что при запуске всего кода в потоке пользовательского интерфейса наш пользовательский интерфейс зависает.Это то, что он хочет, чтобы я решил.Код, вызывающий это, следующий, соответствующий код - просто метод button1_Click_1 (), но я предоставил второй класс для contextx:

 public partial class FrmReplicaAdmin : Form
    {
 private void button1_Click_1(object sender, EventArgs e)
        {

            DataConnectionConfig selectedDatabase = cmboxDatabase.SelectedItem as DataConnectionConfig;

            if (selectedDatabase.PlWorkspace == null)
            {
                statusLabel.Text = "Could not open GeoDatabase (PL)";
                return;
            }

            if (selectedDatabase.DataWorkspace == null)
            {
                statusLabel.Text = "Could not open GeoDatabase (NIS)";
                return;
            }

            int scaleBand = int.Parse(cmboxScale.SelectedItem.ToString());
            string gridName = cmboxGridNr.SelectedItem as string;

            IGeometry shapeOfSelectedAOI = getSelectedPolygon(gridName, scaleBand, selectedDatabase);

            (ArcMap.Application as IMxApplication2).PauseDrawing = true;

            replica.CheckOutReplica(selectedDatabase.PlWorkspace, selectedDatabase.DataWorkspace, selectedDatabase.TemplateGdb, gridName, scaleBand, shapeOfSelectedAOI, selectedDatabase.DatasetName);

            (ArcMap.Application as IMxApplication2).PauseDrawing = false;

        }
}

 public class Replica
    {
 public void CheckOutReplica(IWorkspace plWorkspace, IWorkspace parentWorkspace, string pathToDatabaseTemplate, string gridName, int scaleBand, IGeometry shapeOfSelectedAOI, string datasetName = "NIS.Nautical")
        {
            try
            {
                string replicaName = string.Format("{0}_{1}_r", Environment.UserName, gridName);
                string versionName = string.Format("{0}_{1}", Environment.UserName, gridName);

                string pathToLocalChildDatabase = System.IO.Path.Combine(ReplicationPath, $"{replicaName}.gdb");

                Directory.CreateDirectory(pathToLocalChildDatabase);

                foreach (string newPath in Directory.GetFiles(pathToDatabaseTemplate, "*.*", SearchOption.AllDirectories))
                    File.Copy(newPath, newPath.Replace(pathToDatabaseTemplate, pathToLocalChildDatabase), true);

                IWorkspace childWorkspace = OpenWorkspace(pathToLocalChildDatabase);

                // Create Version in ParentDatabase
                IEnumVersionInfo versionEnum = (parentWorkspace as IVersionedWorkspace).Versions;
                versionEnum.Reset();
                for (IVersionInfo versionInfo = versionEnum.Next(); versionInfo != null; versionInfo = versionEnum.Next())
                {
                    if (versionInfo.VersionName.EndsWith(versionName))
                    {
                        System.Windows.Forms.MessageBox.Show("A version named '" + versionName + "' has already been created", "map...", System.Windows.Forms.MessageBoxButtons.OK);
                        return;
                    }
                }
                Marshal.ReleaseComObject(versionEnum);

                IVersion newVersion = (parentWorkspace as IVersionedWorkspace).DefaultVersion.CreateVersion(versionName);
                newVersion.Access = esriVersionAccess.esriVersionAccessPublic;

                string defQuery = "((IS_CONFLATE=1 AND PLTS_COMP_SCALE >= " + scaleBand + ") OR ((IS_CONFLATE=0 OR IS_CONFLATE IS NULL) AND PLTS_COMP_SCALE = " + scaleBand + "))";
                ReplicateData(parentWorkspace, newVersion.VersionInfo, replicaName, shapeOfSelectedAOI, childWorkspace, defQuery, datasetName);

                // Update map. Show replica data
                ILayerFile nauticalLyrFile = new LayerFileClass();
                nauticalLyrFile.Open(ReplicationPath + @"\Nautical.lyr");

                AddDataToMap((ArcMap.Application.Document as IMxDocument), childWorkspace as IFeatureWorkspace, nauticalLyrFile, gridName, datasetName);
                (ArcMap.Application.Document as IMxDocument).ActiveView.Extent = shapeOfSelectedAOI.Envelope;

                Marshal.ReleaseComObject(childWorkspace);
            }
            catch (Exception err)
            {
                System.Windows.Forms.MessageBox.Show($"Unexpected error. {Environment.NewLine}{err.Message}", "map...", System.Windows.Forms.MessageBoxButtons.OK);
            }
        }
    }


     private void ReplicateData(IWorkspace parentWorkspace, IVersionInfo versionInfo, string replicaName, IGeometry area, IWorkspace childWorkspace, string definitionQuery, string featureDataset)
        {
            if (childWorkspace == null)
                throw new ArgumentNullException("Child workspace is null.");
            if (parentWorkspace == null)
                throw new ArgumentNullException("Parent workspace is null.");
            if (versionInfo == null)
                throw new ArgumentNullException("Version name is null.");
            if (string.IsNullOrEmpty(replicaName))
                throw new ArgumentNullException("Replica name is null.");
            if (area == null)
                throw new ArgumentNullException("Area geometry is null.");

            IVersion oVersion = (parentWorkspace as IVersionedWorkspace).FindVersion(versionInfo.VersionName);
            IWorkspace sdeVersionWorkspace = oVersion as IWorkspace;

            IGeoDataServer parentGds = InitGeoDataServer(sdeVersionWorkspace),
                           childGds = InitGeoDataServer(childWorkspace);

            CreateFeatureDatasetReplica(parentGds, childGds, versionInfo, replicaName, parentWorkspace, childWorkspace, area, definitionQuery, featureDataset);

            Marshal.ReleaseComObject(parentGds);
            Marshal.ReleaseComObject(childGds);
        }

        //Function to create the replica, based on this link http://help.arcgis.com/en/sdk/10.0/arcobjects_net/conceptualhelp/index.html#//0001000003r5000000 
        private void CreateFeatureDatasetReplica(IGeoDataServer parentGDS, IGeoDataServer childGDS, IVersionInfo versionInfo, string replicaName, IWorkspace parentWorkspace, IWorkspace childWorkspace, IGeometry geometry, string definitionQuery, string featureDatasetName)
        {
            IList<string> existingReplicas = ReadExistingReplicas(parentGDS);
            if (existingReplicas.Contains(replicaName.ToUpper()))
            {
                throw new Exception("A replica with the following name has already been created: " + replicaName);
            }

            IEnumDataset datasets = null;
            if (!string.IsNullOrEmpty(featureDatasetName))
            {
                IEnumDataset featureDatasets = parentWorkspace.get_Datasets(esriDatasetType.esriDTFeatureDataset);
                IFeatureDataset featureDataset;
                while ((featureDataset = featureDatasets.Next() as IFeatureDataset) != null)
                {
                    if (featureDataset.Name == featureDatasetName)
                    {
                        datasets = featureDataset.Subsets;
                        break;
                    }
                }
                if (datasets == null)
                    throw new Exception("Didn't find FeatureDataset " + featureDatasetName + " in the db");
            }
            else
            {
                datasets = parentWorkspace.get_Datasets(esriDatasetType.esriDTFeatureClass);
            }

            IGPReplicaDatasets gpReplicaDatasets = new GPReplicaDatasetsClass();
            IDataset dataset;
            while ((dataset = datasets.Next()) != null)
            {
                //temporary workaround to not include a view that is on the feature classes :^)
                if (dataset.Name.Contains("VW_") || dataset.Name.Contains("_EVW"))
                    continue;

                if (m_ListExcludedTables.Contains(dataset.Name.Substring(dataset.Name.LastIndexOf(".") + 1).ToUpper()))
                    continue;

                if (!(childWorkspace as IWorkspace2).NameExists[dataset.Type, dataset.Name.Substring(dataset.Name.LastIndexOf(".") + 1)])
                    continue;

                IGPReplicaDataset gpReplicaDataset = new GPReplicaDatasetClass();
                gpReplicaDataset.DatasetType = dataset.Type;
                gpReplicaDataset.Name = dataset.Name.ToUpper();
                gpReplicaDataset.IsPrivate = false;
                gpReplicaDataset.UseGeometry = true;
                gpReplicaDataset.RowsType = esriRowsType.esriRowsTypeFilter;

                if ((dataset as ITable).Fields.FindField("PLTS_COMP_SCALE") != -1)
                    gpReplicaDataset.DefQuery = definitionQuery; //DefQuery here
                else
                    gpReplicaDataset.DefQuery = "";

                gpReplicaDatasets.Add(gpReplicaDataset);
            }

            IGPReplicaDescription gpReplicaDesc = new GPReplicaDescriptionClass();
            gpReplicaDesc.QueryGeometry = geometry;
            gpReplicaDesc.SpatialRelation = esriSpatialRelEnum.esriSpatialRelIntersects;
            gpReplicaDesc.ModelType = esriReplicaModelType.esriModelTypeSimple;
            gpReplicaDesc.SingleGeneration = true;
            gpReplicaDesc.ReplicaDatasets = gpReplicaDatasets;

            IGPReplicaOptions2 replicaOptions = new GPReplicaOptionsClass();
            replicaOptions.AccessType = esriReplicaAccessType.esriReplicaAccessNone;
            replicaOptions.RegisterReplicaOnly = true;

            ExtractData(datasets, childWorkspace, geometry, definitionQuery);

            IReplicationAgent replicationAgent = new ReplicationAgentClass();
            replicationAgent.CreateReplica(versionInfo.VersionName, parentGDS, childGDS, replicaName, gpReplicaDesc, replicaOptions);
        }
    }

Для исправления зависания пользовательского интерфейса я внес следующие изменения в button1_Click_1() метод:

public partial class FrmReplicaAdmin : Form
    {
     private void button1_Click_1(object sender, EventArgs e)
            {

                DataConnectionConfig selectedDatabase = cmboxDatabase.SelectedItem as DataConnectionConfig;

                if (selectedDatabase.PlWorkspace == null)
                {
                    statusLabel.Text = "Could not open GeoDatabase (PL)";
                    return;
                }

                if (selectedDatabase.DataWorkspace == null)
                {
                    statusLabel.Text = "Could not open GeoDatabase (NIS)";
                    return;
                }

                int scaleBand = int.Parse(cmboxScale.SelectedItem.ToString());
                string gridName = cmboxGridNr.SelectedItem as string;

                IGeometry shapeOfSelectedAOI = getSelectedPolygon(gridName, scaleBand, selectedDatabase);

                // adding inputs to list i can pass onto the backgroundWorker
                List<object> arguments = new List<object>();
                arguments.Add(selectedDatabase.PlWorkspace);
                arguments.Add(selectedDatabase.DataWorkspace);
                arguments.Add(selectedDatabase.TemplateGdb);
                arguments.Add(gridName);
                arguments.Add(scaleBand);
                arguments.Add(shapeOfSelectedAOI);
                arguments.Add(selectedDatabase.DatasetName);

                backgroundWorker1.RunWorkerAsync(arguments);

                // starting progress bar
                progressBarReplica.Visible = true;
                lblReplica.Text = "Checking out replica...";
            }


    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                List<object> genericlist = e.Argument as List<object>;

                IWorkspace ws = (IWorkspace)genericlist[0];
                IWorkspace pWs = (IWorkspace)genericlist[1];
                string pathToDbTemplate = genericlist[2].ToString();
                string gName = genericlist[3].ToString();
                int sBand = (int)genericlist[4];
                IGeometry shape = (IGeometry)genericlist[5];
                string dsName = genericlist[6].ToString();

                (ArcMap.Application as IMxApplication2).PauseDrawing = true;

                replica.CheckOutReplica(ws, pWs, pathToDbTemplate, gName, sBand, shape, dsName);

                (ArcMap.Application as IMxApplication2).PauseDrawing = false;
            }
        }

Это вызывает: "RPC_E_SERVERFAULT (0X80010105)", но пользовательский интерфейс больше не зависает.Я предполагаю, что это потому, что я инициирую базу данных в первом потоке, а затем использую ее во втором.Я также вроде как понимаю, что не могу использовать backgroundWorker из-за всего, что есть в STA и COM-объектах с ArcGis, но я все еще не получаю все это на 100%.

Любая помощь в возможном решении сделать мой пользовательский интерфейс отзывчивой или, по крайней мере, показать какой-то индикатор выполнения во время выполнения задачи, было бы неплохо.Весь процесс может занимать несколько минут, и программа чувствует себя так, как будто она зависла из-за зависания.

Редактировать: Я имею в виду ArcMap, забыл упомянуть об этом.

...