void ExportMetadata()
        {
            int DelayExport      = 2000;                // milliseconds
            int ExportBatchSize  = 1000;                // After each batch is exported, the process will wait for DelayExport and resume (unless cancelled)
            int CurrentBatchSize = 0;

            Func <FCachedDocumentData, bool> AddElements = (FCachedDocumentData CacheData) =>
            {
                while (CacheData.ElementsWithoutMetadata.Count > 0)
                {
                    if (CurrentBatchSize == ExportBatchSize)
                    {
                        // Add some delay before exporting next batch.
                        CurrentBatchSize = 0;

                        // Send metadata to DirectLink.
                        DatasmithScene.BuildScene(SceneName);
                        DatasmithDirectLink.UpdateScene(DatasmithScene);

                        MetadataEvent.WaitOne(DelayExport);
                    }

                    if (MetadataCancelToken.IsCancellationRequested)
                    {
                        return(false);
                    }

                    var Entry = CacheData.ElementsWithoutMetadata.Dequeue();

                    // Handle the case where element might be deleted in the main export path.
                    if (!CacheData.CachedElements.ContainsKey(Entry.Key))
                    {
                        continue;
                    }

                    Element RevitElement = CacheData.SourceDocument.GetElement(Entry.Key);

                    if (RevitElement == null)
                    {
                        continue;
                    }

                    FDocumentData.FBaseElementData ElementData = Entry.Value;
                    FDatasmithFacadeActor          Actor       = ElementData.ElementActor;

                    ElementData.ElementMetaData = new FDatasmithFacadeMetaData(Actor.GetName() + "_DATA");
                    ElementData.ElementMetaData.SetLabel(Actor.GetLabel());
                    ElementData.ElementMetaData.SetAssociatedElement(Actor);

                    FDocumentData.AddActorMetadata(RevitElement, ElementData.ElementMetaData);

                    ++CurrentBatchSize;

#if DEBUG
                    Debug.WriteLine($"metadata batch element {CurrentBatchSize}, remain in Q {CacheData.ElementsWithoutMetadata.Count}");
#endif
                }

                return(true);
            };

            List <FCachedDocumentData> CachesToExport = new List <FCachedDocumentData>();

            Action <FCachedDocumentData> GetLinkedDocuments = null;

            GetLinkedDocuments = (FCachedDocumentData InParent) =>
            {
                CachesToExport.Add(InParent);
                foreach (var Cache in InParent.LinkedDocumentsCache.Values)
                {
                    GetLinkedDocuments(Cache);
                }
            };

            GetLinkedDocuments(RootCache);

            foreach (var Cache in CachesToExport)
            {
                bool Success = AddElements(Cache);
                if (!Success)
                {
#if DEBUG
                    Debug.WriteLine("metadata cancelled");
#endif
                    return;                     // Metadata export was cancelled.
                }
            }

            if (CurrentBatchSize > 0)
            {
                // Send remaining chunk of metadata.
                DatasmithScene.BuildScene(SceneName);
                DatasmithDirectLink.UpdateScene(DatasmithScene);
            }

#if DEBUG
            Debug.WriteLine("metadata exported");
#endif
        }
        private static void AddMetadataToDatasmithActor(FDatasmithFacadeActor InDatasmithActor, RhinoSceneHierarchyNode InNode, FDatasmithFacadeScene InDatasmithScene)
        {
            if (!InNode.Info.bHasRhinoObject)
            {
                return;
            }

            RhinoObject         NodeObject  = InNode.Info.RhinoModelComponent as RhinoObject;
            NameValueCollection UserStrings = NodeObject.Attributes.GetUserStrings();

            if (UserStrings != null && UserStrings.Count > 0)
            {
                string[] Keys = UserStrings.AllKeys;
                FDatasmithFacadeMetaData DatasmithMetaData = new FDatasmithFacadeMetaData(InDatasmithActor.GetName() + "_DATA");
                DatasmithMetaData.SetLabel(InDatasmithActor.GetLabel());
                DatasmithMetaData.SetAssociatedElement(InDatasmithActor);

                for (int KeyIndex = 0; KeyIndex < Keys.Length; ++KeyIndex)
                {
                    string CurrentKey     = Keys[KeyIndex];
                    string EvaluatedValue = FDatasmithRhinoUtilities.EvaluateAttributeUserText(InNode, UserStrings.Get(CurrentKey));

                    DatasmithMetaData.AddPropertyString(CurrentKey, EvaluatedValue);
                }

                InDatasmithScene.AddMetaData(DatasmithMetaData);
            }
        }