Exemplo n.º 1
0
        public static IList <esriSegmentInfo> CalculateShortSegments(
            [NotNull] FeatureVertexInfo forFeatureVertexInfo,
            bool use2DLengthOnly,
            [CanBeNull] IGeometry perimeter)
        {
            _msg.VerboseDebugFormat("Getting short segments for {0}",
                                    GdbObjectUtils.ToString(forFeatureVertexInfo.Feature));

            var minimumSegmentLength =
                (double)Assert.NotNull(forFeatureVertexInfo.MinimumSegmentLength);

            IList <esriSegmentInfo> shortSegments = GetShortSegments(
                forFeatureVertexInfo.Feature, perimeter, minimumSegmentLength, use2DLengthOnly);

            IList <esriSegmentInfo> protectedSegments = GetProtectedSegments(
                shortSegments, forFeatureVertexInfo.CrackPointCollection);

            IList <esriSegmentInfo> removableSegments =
                shortSegments.Where(shortSegment => !protectedSegments.Contains(shortSegment))
                .ToList();

            forFeatureVertexInfo.ShortSegments = removableSegments;

            forFeatureVertexInfo.NonRemovableShortSegments = protectedSegments;

            return(removableSegments);
        }
Exemplo n.º 2
0
        public void RestoreSelection(
            [NotNull] BoundDataGridSelectionState <ROW> selectionState)
        {
            Assert.ArgumentNotNull(selectionState, nameof(selectionState));

            if (_msg.IsVerboseDebugEnabled)
            {
                _msg.VerboseDebugFormat("Restoring selection ({0} row(s))",
                                        selectionState.SelectionCount);
            }

            DataGridViewUtils.RestoreSelection(_dataGridView, selectionState);
        }
        public CodedDomainValueProvider([NotNull] ITable table,
                                        [NotNull] string codedDomainField,
                                        [CanBeNull] IFormatProvider formatProvider)
        {
            _codedFieldIndex = table.FindField(codedDomainField);

            IField field  = table.Fields.Field[_codedFieldIndex];
            var    domain = (ICodedValueDomain)field.Domain;

            _valueDict = new Dictionary <object, T?>();
            foreach (CodedValue codedValue in DomainUtils.GetCodedValueList(domain))
            {
                object code = codedValue.Value;
                string name = codedValue.Name;

                try
                {
                    var value =
                        (T)Assert.NotNull(Convert.ChangeType(name, typeof(T), formatProvider));

                    _valueDict.Add(code, value);
                }
                catch (Exception e)
                {
                    _msg.VerboseDebugFormat("Unable to convert '{0}' to type {1}: {2}",
                                            name, typeof(T).Name, e.Message);
                    _valueDict.Add(code, null);
                }
            }
        }
        private void ProlongThirdEdge([NotNull] IFeature thirdEdge,
                                      [NotNull] IPolyline cutOffLine)
        {
            Assert.ArgumentNotNull(thirdEdge, nameof(thirdEdge));

            if (_msg.IsVerboseDebugEnabled)
            {
                _msg.VerboseDebugFormat(
                    "The third connected edge {0} is prolonged with cut-off line {1}",
                    GdbObjectUtils.ToString(thirdEdge), GeometryUtils.ToString(cutOffLine));
            }

            // add the cut off line to the third edge, take along the junction
            // (if using MaintainConnectivityStretchLastSegment here, it can result in
            //  COM-exception 0x80044020)
            // TODO: Handle Z-differences according to some option (Union always merges and higher Z-value wins)
            var originalLine  = (IPolyline)thirdEdge.ShapeCopy;
            var prolongedLine = (IPolyline)GeometryUtils.Union(originalLine, cutOffLine);

            // NOTE: The cutOffLine and the original thirdEdgeLine can have different orientation.
            //		 The union sometimes produces lines that use the cutOffLine's orientation
            //		 Maintaining the orientation of the third edge is crucial to avoid COM-exception 0x80044020
            EnsureOrientation(originalLine, prolongedLine);

            StoreSingleFeatureShape(thirdEdge, prolongedLine);

            AddToRefreshArea(prolongedLine);
        }
Exemplo n.º 5
0
        private static bool TryGetVersion([CanBeNull] out string version)
        {
            try
            {
                // the following code fails in the 64bit background environment (see COM-221)
                RuntimeInfo runtime = RuntimeManager.ActiveRuntime;

                if (runtime == null)
                {
                    // not bound yet?

                    // There seems to be another scenario where this is null (observed
                    // for background gp on a particular setup, which also includes server).

                    _msg.Debug(
                        "RuntimeInfo not available. Trying to get version from assembly");

                    if (TryGetVersionFromVersionAssembly(out version))
                    {
                        return(true);
                    }

                    _msg.DebugFormat("Unable to get ArcGIS version from assembly");
                    version = null;
                    return(false);
                }

                version = runtime.Version;
                return(true);
            }
            catch (DllNotFoundException e)
            {
                _msg.VerboseDebugFormat(
                    "Error accessing RuntimeManager: {0}; trying to get version from assembly",
                    e.Message);

                if (TryGetVersionFromVersionAssembly(out version))
                {
                    return(true);
                }

                _msg.DebugFormat("Unable to get ArcGIS version from assembly");
                return(false);
            }
        }
Exemplo n.º 6
0
        public static Assembly LoadAssembly([NotNull] string assemblyName)
        {
            Assert.ArgumentNotNullOrEmpty(assemblyName, nameof(assemblyName));

            AssemblyName name = GetAssemblyName(BinDirectory, assemblyName);

            if (string.IsNullOrEmpty(name.CodeBase))
            {
                _msg.VerboseDebugFormat("Loading assembly from {0}", name);
            }
            else
            {
                _msg.VerboseDebugFormat("Loading assembly from {0} (codebase: {1})",
                                        name, name.CodeBase);
            }

            return(Assembly.Load(name));
        }
Exemplo n.º 7
0
        public static Assembly LoadAssembly(
            [NotNull] string assemblyName,
            [CanBeNull] IReadOnlyDictionary <string, string> assemblySubstitutes = null)
        {
            IReadOnlyDictionary <string, string> substitutes = GetSubstitutes(assemblySubstitutes);

            bool throwOnError =
                !substitutes.TryGetValue(assemblyName, out string substituteAssembly);

            Assembly assembly;

            try
            {
                AssemblyName name = GetAssemblyName(BinDirectory, assemblyName);

                if (string.IsNullOrEmpty(name.CodeBase))
                {
                    _msg.VerboseDebugFormat("Loading assembly from {0}", name);
                }
                else
                {
                    _msg.VerboseDebugFormat("Loading assembly from {0} (codebase: {1})",
                                            name, name.CodeBase);
                }

                assembly = Assembly.Load(name);
            }
            catch (Exception e)
            {
                _msg.Debug($"Loading {assemblyName} from {BinDirectory} failed.", e);

                if (throwOnError)
                {
                    throw;
                }

                _msg.DebugFormat("Trying assembly substitute {0}...", substituteAssembly);

                assembly = Assembly.Load(substituteAssembly);
            }

            return(assembly);
        }
Exemplo n.º 8
0
        public static bool UseCaseSensitiveSql([NotNull] ITable table,
                                               SqlCaseSensitivity caseSensitivity)
        {
            switch (caseSensitivity)
            {
            case SqlCaseSensitivity.CaseInsensitive:
                if (_msg.IsVerboseDebugEnabled)
                {
                    _msg.VerboseDebugFormat("{0}: not case sensitive",
                                            DatasetUtils.GetName(table));
                }

                return(false);

            case SqlCaseSensitivity.CaseSensitive:
                if (_msg.IsVerboseDebugEnabled)
                {
                    _msg.VerboseDebugFormat("{0}: case sensitive",
                                            DatasetUtils.GetName(table));
                }

                return(true);

            case SqlCaseSensitivity.SameAsDatabase:
                var  sqlSyntax = DatasetUtils.GetWorkspace(table) as ISQLSyntax;
                bool result    = sqlSyntax != null && sqlSyntax.GetStringComparisonCase();

                if (_msg.IsVerboseDebugEnabled)
                {
                    _msg.VerboseDebugFormat(sqlSyntax == null
                                                                                ? "{0}: database case sensitivity: UNKNOWN (use {1})"
                                                                                : "{0}: database case sensitivity: {1}",
                                            DatasetUtils.GetName(table), result);
                }

                return(result);

            default:
                throw new InvalidOperationException(
                          string.Format("Unsupported SqlCaseSensitivity: {0}", caseSensitivity));
            }
        }
Exemplo n.º 9
0
        public override async Task VerifyStandaloneXml(
            StandaloneVerificationRequest request,
            IServerStreamWriter <StandaloneVerificationResponse> responseStream,
            ServerCallContext context)
        {
            try
            {
                await StartRequest(context.Peer, request);

                _msg.InfoFormat("Starting stand-alone verification request from {0}",
                                context.Peer);
                _msg.VerboseDebugFormat("Request details: {0}", request);

                Action <LoggingEvent> action =
                    SendInfoLogAction(responseStream, ServiceCallStatus.Running);

                using (MessagingUtils.TemporaryRootAppender(new ActionAppender(action)))
                {
                    Func <ITrackCancel, ServiceCallStatus> func =
                        trackCancel => VerifyStandaloneXmlCore(request, responseStream,
                                                               trackCancel);

                    ServiceCallStatus result =
                        await GrpcServerUtils.ExecuteServiceCall(
                            func, context, _staThreadScheduler);

                    _msg.InfoFormat("Verification {0}", result);
                }
            }
            catch (Exception e)
            {
                _msg.Error($"Error verifying quality for request {request}", e);

                SendFatalException(e, responseStream);
                SetUnhealthy();
            }
            finally
            {
                EndRequest();
            }
        }
Exemplo n.º 10
0
        private void ApplyFormState([NotNull] T formState,
                                    FormStateRestoreOption restoreOption)
        {
            _msg.VerboseDebugFormat("Applying form state for {0}", Form.Name);

            using (_msg.IncrementIndentation())
            {
                switch (restoreOption)
                {
                case FormStateRestoreOption.OnlyLocation:
                    ApplyLocation(formState);
                    ApplyTopMost(formState);
                    // don't apply size
                    // don't apply window state
                    ApplyInternalFormState(formState);
                    break;

                case FormStateRestoreOption.KeepLocation:
                    // don't apply location
                    ApplyTopMost(formState);
                    ApplySize(formState);
                    ApplyWindowState(formState);
                    ApplyInternalFormState(formState);
                    break;

                case FormStateRestoreOption.Normal:
                    ApplyLocation(formState);
                    ApplyTopMost(formState);
                    ApplySize(formState);
                    ApplyWindowState(formState);
                    ApplyInternalFormState(formState);
                    break;

                default:
                    throw new ArgumentException(
                              string.Format("Unknown restore option: {0}", restoreOption));
                }
            }
        }
Exemplo n.º 11
0
        public int Compare(object value1, object value2, int fieldIndex, int fieldSortIndex)
        {
            // TODO: sometimes the OID gets provided (with fieldSortIndex == 1) -> we never asked for it!
            //		 test whether we can safely ignore it by if (fieldSortIndex > 0) return 0;
            try
            {
                if (fieldSortIndex > 0)
                {
                    _msg.VerboseDebugFormat(
                        "Assuming equal IDs: value1 {0}, value2 {1}, fieldIndex {2}, fieldSortIndex {3}",
                        value1, value2, fieldIndex, fieldSortIndex);

                    return(0);
                }

                if (Convert.IsDBNull(value1))
                {
                    _msg.WarnFormat("Sort field (fieldindex {0}) contains NULL (value1).",
                                    fieldIndex);

                    return(Convert.IsDBNull(value2) ? 0 : -1);
                }

                if (Convert.IsDBNull(value2))
                {
                    _msg.WarnFormat("Sort field (fieldindex {0}) contains NULL (value2).",
                                    fieldIndex);

                    return(Convert.IsDBNull(value1) ? 0 : 1);
                }

                // NOTE: To avoid exception with explicit cast
                // TODO: Check performance
                string value1String = Convert.ToString(value1);
                string value2String = Convert.ToString(value2);

                return(string.Compare(value1String, value2String,
                                      StringComparison.InvariantCultureIgnoreCase));
            }
            catch (Exception e)
            {
                _msg.Debug(
                    string.Format("Error comparing object values {0} and {1}", value1, value2), e);
                throw;
            }
        }
Exemplo n.º 12
0
        private static bool TryGetInstallDirForActiveRuntime(out string installDir)
        {
            installDir = null;

            try
            {
                // the following code fails in the 64bit background environment (see COM-221)
                RuntimeInfo runtime = RuntimeManager.ActiveRuntime;

                if (runtime == null)
                {
                    // not bound yet?

                    // There seems to be another scenario where this is null (observed
                    // for background gp on a particular setup, which also includes server).
                    _msg.Debug("RuntimeInfo not available.");

                    return(false);
                }

                if (Process.GetCurrentProcess().ProcessName.Equals("python"))
                {
                    // in x64-bit python the active runtime is Server (even if background GP is installed too).
                    // However, the (contour) GP tool fails (E_FAIL, TOP-5169) unless the Desktop(!) toolbox is referenced.
                    _msg.DebugFormat(
                        "Called from python. The active runtime ({0}) might be incorrect -> using default implementation, favoring Desktop.",
                        runtime.Product);
                    return(false);
                }

                installDir = runtime.Path;
                return(true);
            }
            catch (DllNotFoundException e)
            {
                _msg.VerboseDebugFormat(
                    "Error accessing RuntimeManager: {0}.",
                    e.Message);

                return(false);
            }
        }
Exemplo n.º 13
0
        public bool TryWritePendingChanges()
        {
            try
            {
                if (!_hasPendingChanges)
                {
                    _msg.VerboseDebug("no pending changes");
                    return(false);
                }

                if (AutoSave)
                {
                    _canSave = true;
                }

                if (!CanHandlePendingChanges())
                {
                    _msg.VerboseDebugFormat(
                        "Cannot handle pending changes. Reason: saving {0}, aborting {1}, can save {2}",
                        _saving,
                        _aborting, _canSave);
                    return(false);
                }

                BeginWrite();

                WriteChanges();
                return(true);
            }

            catch (Exception e)
            {
                ErrorHandler.HandleError(e.Message, e, _msg, _owner);
                return(false);
            }
            finally
            {
                _canSave = false;
                EndWrite();
            }
        }
Exemplo n.º 14
0
        public void Callback()
        {
            if (_pending)
            {
                _msg.VerboseDebug("Callback already pending - ignored");
                return;
            }

            _expectedThreadId = Thread.CurrentThread.ManagedThreadId;

            if (_synchronizationContext == null)
            {
                // get the winforms synchronization context
                // NOTE: relying on the current synchronization context of the current thread is not sufficient,
                //       as this gets changed depending on previous winforms actions:
                //       - after showing a modal dialog, the synchronization context DOES NOT displatch back to the gui thread
                //       - after opening/activating a modeless form, it DOES dispatch back to the gui thread
                // -> better to set the expected synchronization context explicitly
                // NOTE: this must be done on the gui thread, otherwise even this synchronization context will not dispatch back to it
                _synchronizationContext = new WindowsFormsSynchronizationContext();
            }

            _msg.VerboseDebugFormat("Scheduling delayed processing (thread: {0})",
                                    Thread.CurrentThread.ManagedThreadId);

            try
            {
                _pending = true;

                _synchronizationContext.Post(delegate { TryExecuteProcedure(); }, null);
            }
            catch (Exception ex)
            {
                _pending = false;
                _msg.Error($"Error executing procedure via synchronization context: {ex.Message}",
                           ex);
            }
        }
Exemplo n.º 15
0
        public void Add(T identifier, IEnumerable <TileIndex> intersectedTiles)
        {
            foreach (TileIndex intersectedTileIdx in intersectedTiles)
            {
                List <T> tileGeometryRefs;
                if (!_tiles.TryGetValue(intersectedTileIdx, out tileGeometryRefs))
                {
                    tileGeometryRefs = new List <T>(_estimatedItemsPerTile);

                    _tiles.Add(intersectedTileIdx, tileGeometryRefs);
                }

                if (_msg.IsVerboseDebugEnabled &&
                    tileGeometryRefs.Count >= _estimatedItemsPerTile)
                {
                    _msg.VerboseDebugFormat(
                        "Numer of items in tile {0} is exceeding the estimated maximum and now contains {1} items",
                        intersectedTileIdx, tileGeometryRefs.Count + 1);
                }

                tileGeometryRefs.Add(identifier);
            }
        }
Exemplo n.º 16
0
        private static IEnumerable <CutSubcurve> GetIndividualGeometriesReshapeCurves(
            [NotNull] IList <IFeature> sourceFeatures,
            [NotNull] IPolyline targetPolyline,
            [NotNull] ISubcurveCalculator subcurveCalculator)
        {
            var result = new List <CutSubcurve>();

            _msg.DebugFormat(
                "GetIndividualGeometriesReshapeCurves: calculating curves for {0} geometries..",
                sourceFeatures.Count);

            foreach (IFeature sourceFeature in sourceFeatures)
            {
                IGeometry sourceGeometry = sourceFeature.Shape;

                var individualResultList = new List <CutSubcurve>();
                ReshapeAlongCurveUsability individualResult =
                    subcurveCalculator.CalculateSubcurves(
                        sourceGeometry, targetPolyline, individualResultList, null);

                foreach (CutSubcurve subcurve in individualResultList)
                {
                    subcurve.Source = new GdbObjectReference(sourceFeature);
                }

                result.AddRange(individualResultList);

                _msg.VerboseDebugFormat(
                    "Individual geometry's subcurve calculation result: {0}",
                    individualResult);

                Marshal.ReleaseComObject(sourceGeometry);
            }

            return(result);
        }
Exemplo n.º 17
0
        private bool IsDisjoint([NotNull] IGeometry shape,
                                [NotNull] ITable relatedTable, int relatedTableIndex,
                                [NotNull] QueryFilterHelper relatedFilterHelper,
                                [NotNull] IGeometry cacheShape)
        {
            ISpatialFilter intersectsFilter = _spatialFiltersIntersects[relatedTableIndex];

            intersectsFilter.Geometry = shape;

            foreach (
                IRow row in
                Search(relatedTable, intersectsFilter, relatedFilterHelper, cacheShape))
            {
                if (_msg.IsVerboseDebugEnabled)
                {
                    _msg.VerboseDebugFormat("not disjoint (row found: {0})",
                                            GdbObjectUtils.ToString(row));
                }

                return(false);
            }

            return(true);
        }
Exemplo n.º 18
0
        public static IEnumerable <IGeometry> GetSelectableOverlaps(
            [NotNull] IFeature sourceFeature,
            [NotNull] SpatialHashSearcher <IFeature> overlappingFeatures,
            [CanBeNull] NotificationCollection notifications = null,
            [CanBeNull] ITrackCancel trackCancel             = null)
        {
            IGeometry sourceGeometry = sourceFeature.Shape;

            if (sourceGeometry == null || sourceGeometry.IsEmpty)
            {
                yield break;
            }

            IEnvelope sourceEnvelope = sourceGeometry.Envelope;

            double tolerance = GeometryUtils.GetXyTolerance(sourceGeometry);

            foreach (IFeature targetFeature in overlappingFeatures.Search(
                         sourceEnvelope.XMin, sourceEnvelope.YMin,
                         sourceEnvelope.XMax, sourceEnvelope.YMax, tolerance))
            {
                if (trackCancel != null && !trackCancel.Continue())
                {
                    yield break;
                }

                _msg.VerboseDebugFormat("Calculating overlap from {0}",
                                        GdbObjectUtils.ToString(targetFeature));

                IGeometry targetGeometry = targetFeature.Shape;

                if (GeometryUtils.Disjoint(targetGeometry, sourceGeometry))
                {
                    continue;
                }

                if (GeometryUtils.Contains(targetGeometry, sourceGeometry))
                {
                    // Idea for the future: Optionally allow also deleting features (probably using a black display feedback)
                    NotificationUtils.Add(notifications,
                                          "Source feature {0} is completely within target {1} and would become empty if the overlap was removed. The overlap is supressed.",
                                          RowFormat.Format(sourceFeature),
                                          RowFormat.Format(targetFeature));
                    continue;
                }

                if (sourceGeometry.GeometryType == esriGeometryType.esriGeometryMultiPatch)
                {
                    sourceGeometry = GeometryFactory.CreatePolygon(sourceGeometry);
                }

                IGeometry intersection = TryGetIntersection(sourceGeometry, targetGeometry);

                if (intersection == null)
                {
                    continue;
                }

                if (GeometryUtils.GetPartCount(intersection) > 1)
                {
                    foreach (var part in GeometryUtils.Explode(intersection))
                    {
                        yield return(part);
                    }
                }
                else
                {
                    yield return(intersection);
                }
            }
        }
Exemplo n.º 19
0
        private static IEnumerable <CutSubcurve> GetCutSubcurves(
            [NotNull] IGeometryCollection differences,
            int differencePathIndexToSplit,
            [NotNull] IGeometry cuttingGeometry,
            IDictionary <SubcurveNode, SubcurveNode> nodes,
            [NotNull] IPointCollection intersectionPoints,
            IPolyline target)
        {
            var curveToSplit = (IPath)differences.Geometry[differencePathIndexToSplit];

            IGeometry highLevelCurveToSplit = GeometryUtils.GetHighLevelGeometry(
                curveToSplit,
                true);

            // Enlarge search tolerance to avoid missing points because the intersection points are
            // 'found' between the target vertex and the actual source-target intersection.
            // But when using minimum tolerance, make sure we're not searching with the normal tolerance
            // otherwise stitch points close to target vertices are missed and the result becomes unnecessarily
            // inaccurate by the two vertices being simplified (at data tolerance) into one intermediate.
            double searchToleranceFactor = 2 * Math.Sqrt(2);
            double stitchPointSearchTol  =
                GeometryUtils.GetXyTolerance((IGeometry)differences) *
                searchToleranceFactor;

            if (intersectionPoints.PointCount == 0 ||
                GeometryUtils.Disjoint(cuttingGeometry, highLevelCurveToSplit))
            {
                _msg.VerboseDebugFormat(
                    "GetCutSubcurves: No intersections / disjoint geometries");

                yield return
                    (CreateCutSubcurve(curveToSplit, null, nodes, null, target,
                                       stitchPointSearchTol));
            }
            else
            {
                _msg.DebugFormat("GetCutSubcurves: Intersection Point Count: {0}.",
                                 intersectionPoints.PointCount);

                const bool projectPointsOntoPathToSplit = false;

                // NOTE: take tolerance from cutting geometry because the difference's spatial reference could
                // have been changed to minimum tolerance which sometimes results in missed points due to cutOffDistance being too small
                double cutOffDistance = GeometryUtils.GetXyTolerance(cuttingGeometry);

                // the original subdivision at start/end point is not needed any more: merge if it doesn't touch
                // any other difference part (to allow the other difference part to become a reshape candidate)
                IGeometryCollection subCurves = GeometryUtils.SplitPath(
                    curveToSplit, intersectionPoints, projectPointsOntoPathToSplit,
                    cutOffDistance,
                    splittedCurves => UnlessTouchesOtherPart(splittedCurves, differences,
                                                             differencePathIndexToSplit));

                for (var i = 0; i < subCurves.GeometryCount; i++)
                {
                    var subCurve = (IPath)subCurves.Geometry[i];

                    bool?curveTouchesDifferentParts = SubCurveTouchesDifferentParts(
                        subCurve,
                        cuttingGeometry);

                    IPath subCurveClone = GeometryFactory.Clone(subCurve);

                    yield return
                        (CreateCutSubcurve(subCurveClone, intersectionPoints, nodes,
                                           curveTouchesDifferentParts, target,
                                           stitchPointSearchTol));
                }

                Marshal.ReleaseComObject(subCurves);
            }
        }
Exemplo n.º 20
0
        internal static IKeySet ReadKeySet([NotNull] ITable table,
                                           [NotNull] string keyField,
                                           [CanBeNull] string whereClause,
                                           esriFieldType keyFieldType,
                                           int keyFieldIndex)
        {
            Assert.ArgumentNotNull(table, nameof(table));
            Assert.ArgumentNotNullOrEmpty(keyField, nameof(keyField));

            Stopwatch       watch       = null;
            MemoryUsageInfo memoryUsage = null;

            if (_msg.IsVerboseDebugEnabled)
            {
                watch       = _msg.DebugStartTiming();
                memoryUsage = new MemoryUsageInfo();
                memoryUsage.Refresh();
            }

            IKeySet result = CreateKeySet(keyFieldType);

            var queryFilter = new QueryFilterClass
            {
                SubFields   = keyField,
                WhereClause = whereClause
            };
            string tableName = DatasetUtils.GetName(table);

            const bool recycle = true;

            foreach (IRow row in GdbQueryUtils.GetRows(table, queryFilter, recycle))
            {
                object key = row.Value[keyFieldIndex];

                if (key == DBNull.Value || key == null)
                {
                    continue;
                }

                // TODO handle errors (e.g. invalid guid strings)
                bool added = result.Add(key);

                if (!added)
                {
                    _msg.VerboseDebugFormat(
                        "Ignored duplicate key found in field '{0}' in table '{1}': {2}",
                        keyField, tableName, key);
                }
            }

            if (watch != null)
            {
                _msg.DebugStopTiming(watch,
                                     "Reading {0:N0} {1} keys from field '{2}' in table '{3}'",
                                     result.Count, keyFieldType, keyField,
                                     DatasetUtils.GetName(table));
                _msg.DebugFormat("Memory usage of keys: {0}", memoryUsage);
            }

            return(result);
        }