/// <summary>
        /// Use to retrieve the contents of a text file under version control.
        /// </summary>
        /// <remarks>
        /// Puts the content of the specified file in a temporary file. The caller is responsible for deleting the file.
        /// </remarks>
        /// <param name="eid">The file's element ID.</param>
        /// <param name="depot">The depot.</param>
        /// <param name="ver_spec">The version specification in the numeric or text format, e.g. \c 32/1 or \c PG_MAINT1_barnyrd\4.</param>
        /// <returns>A string initialized to the name of the temporary file with the contents from the
        /// AccuRev <tt>cat -v \<ver_spec\> -p \<depot\> -e \<eid\></tt> command on success, otherwise \e null on error.
        /// </returns>
        /*! \cat_ <tt>cat -v \<ver_spec\> -p \<depot\> -e \<eid\></tt> */
        /// <exception cref="AcUtilsException">caught and [logged](@ref AcUtils#AcDebug#initAcLogging)
        /// in <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on \c cat command failure.</exception>
        /// <exception cref="Exception">caught and logged in same on failure to handle a range of exceptions.</exception>
        /*! \accunote_ The CLI \c cat command fails on Windows for ptext files larger than 62,733 bytes. Fixed in 6.0. AccuRev defect 28177. */

        /*! \accunote_ For the \c name, \c anc and \c cat commands, the validity of the results is guaranteed only if the commands are issued outside a workspace, or in a workspace whose
         *   backing stream is in the same depot for both workspace and the <tt>-v \<ver_spec\></tt> option used. If issued from a workspace that has a different backing stream than
         *   the <tt>-v \<ver_spec\></tt>, provided both workspace and ver_spec share the same depot, the command results can be deemed valid. However, if issued from a workspace whose
         *   backing stream is in a different depot than the <tt>-v \<ver_spec\></tt>, the command results are invalid. Applications should set the default directory to a non-workspace
         *   location prior to issuing these commands. AccuRev defects 18080, 21469, 1097778. */
        public static async Task <string> getCatFileAsync(int eid, AcDepot depot, string ver_spec)
        {
            string tmpFile = null;

            try
            {
                AcResult r = await AcCommand.runAsync($@"cat -v ""{ver_spec}"" -p ""{depot}"" -e {eid}")
                             .ConfigureAwait(false);

                if (r != null && r.RetVal == 0)
                {
                    tmpFile = Path.GetTempFileName();
                    using (StreamWriter streamWriter = new StreamWriter(tmpFile))
                    {
                        streamWriter.Write(r.CmdResult);
                    }
                }
            }

            catch (AcUtilsException ecx)
            {
                AcDebug.Log($"AcUtilsException caught and logged in AcQuery.getCatFileAsync{Environment.NewLine}{ecx.Message}");
            }

            catch (Exception ecx) // IOException, DirectoryNotFoundException, PathTooLongException, SecurityException... others
            {
                AcDebug.Log($"Exception caught and logged in AcQuery.getCatFileAsync{Environment.NewLine}{ecx.Message}");
            }

            return(tmpFile);
        }
Esempio n. 2
0
        /// <summary>
        /// Populate this container with AcStream objects as per constructor parameters.
        /// AcStream objects are instantiated during AcDepot and AcDepots construction.
        /// This function is called internally and not by user code.
        /// </summary>
        /// <param name="depot">The depot for which streams will be created.</param>
        /// <param name="listfile">List file <tt>\%APPDATA\%\\AcTools\\<prog_name\>\\<depot_name\>.streams</tt> for the
        /// <a href="https://www.microfocus.com/documentation/accurev/72/WebHelp/wwhelp/wwhimpl/js/html/wwhelp.htm#href=AccuRev_User_CLI/cli_ref_show.html">show -l <list-file> streams</a>
        /// command if found, otherwise \e null.<br>
        /// Example: <tt>"C:\Users\barnyrd\AppData\Roaming\AcTools\FooApp\NEPTUNE.streams"</tt>.</param>
        /// <returns>\e true if initialization succeeded, \e false otherwise.</returns>
        /// <exception cref="AcUtilsException">caught and [logged](@ref AcUtils#AcDebug#initAcLogging)
        /// in <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on \c show command failure.</exception>
        /// <exception cref="Exception">caught and [logged](@ref AcUtils#AcDebug#initAcLogging)
        /// in <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on failure
        /// to handle a range of exceptions.</exception>
        /*! \sa [AcStreams constructor](@ref AcUtils#AcStreams#AcStreams) */
        /*! \show_ <tt>show \<-fxig|-fxg\> -p \<depot\> [-l listfile] streams</tt> */

        /*! \accunote_ The following XML attributes from <tt>show -fx -p \<depot\> streams</tt> may exist depending on the version of AccuRev in use.
         * They are used internally by AccuRev and are not intended for customer usage. Micro Focus incident #3132463.
         * - \e eventStream: Efficient way to determine if the server process needs to fire the event trigger processing.
         * - \e eventStreamHWM: Used by GitCentric to track the high watermark for synchronization.
         * - \e hasProperties: Efficient way for the GUI to determine if the property icon is displayed in the StreamBrowser. */
        internal async Task <bool> initAsync(AcDepot depot, string listfile = null)
        {
            bool ret = false; // assume failure

            try
            {
                AcResult result = await AcCommand.runAsync($@"show {(_includeHidden ? "-fxig" : "-fxg")} -p ""{depot}""" +
                                                           $@"{((listfile == null) ? String.Empty : "-l " + "" + listfile + "")} streams")
                                  .ConfigureAwait(false);

                if (result != null && result.RetVal == 0)
                {
                    XElement xml = XElement.Parse(result.CmdResult);
                    IEnumerable <XElement> filter = null;
                    if (_dynamicOnly)
                    {
                        filter = from s in xml.Elements("stream")
                                 where (bool)s.Attribute("isDynamic") == true select s;
                    }
                    else
                    {
                        filter = from s in xml.Elements("stream") select s;
                    }

                    foreach (XElement e in filter)
                    {
                        AcStream stream = new AcStream();
                        stream.Name      = (string)e.Attribute("name");
                        stream.ID        = (int)e.Attribute("streamNumber");
                        stream.BasisName = (string)e.Attribute("basis") ?? String.Empty;
                        stream.BasisID   = (int?)e.Attribute("basisStreamNumber") ?? -1;
                        stream.Depot     = depot;
                        stream.IsDynamic = (bool)e.Attribute("isDynamic");
                        string type = (string)e.Attribute("type");
                        stream.Type = (StreamType)Enum.Parse(typeof(StreamType), type);
                        // attribute "time" only exists for snapshot streams (creation date) or when a dynamic stream has a time basis
                        stream.Time      = e.acxTime("time"); // time basis
                        stream.StartTime = (DateTime)e.acxTime("startTime");
                        // hidden attribute exists only if the stream is hidden
                        stream.Hidden          = (e.Attribute("hidden") != null);
                        stream.HasDefaultGroup = (bool)e.Attribute("hasDefaultGroup");
                        lock (_locker) { Add(stream); }
                    }

                    ret = true; // operation succeeded
                }
            }

            catch (AcUtilsException ecx)
            {
                AcDebug.Log($"AcUtilsException caught and logged in AcStreams.initAsync{Environment.NewLine}{ecx.Message}");
            }

            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in AcStreams.initAsync{Environment.NewLine}{ecx.Message}");
            }

            return(ret);
        }
        /// <summary>
        /// Convert AcResult.CmdResult XML from \e result into AcWorkspace objects and add them to our container.
        /// Filters out workspace/ref trees that are not in the user's default list of depots they can view based on ACL permissions.
        /// </summary>
        /// <param name="result">Previously initialized AcResult object from AcWorkspaces.getWorkspacesXMLAsync and AcWorkspaces.getReferenceTreesXMLAsync.</param>
        /// <param name="depot">Specify a depot to limit the query to workspaces and ref trees in \e depot only, not the users default list of depots.</param>
        /// <returns>\e true if object initialization succeeded, \e false otherwise.</returns>
        /// <exception cref="Exception">caught and [logged](@ref AcUtils#AcDebug#initAcLogging)
        /// in <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on failure to handle a range of exceptions.</exception>
        private bool storeWSpaces(AcResult result, AcDepot depot = null)
        {
            bool ret = false; // assume failure

            try
            {
                // filter out workspace/ref trees not in the user's default list
                XElement xml = XElement.Parse(result.CmdResult);
                IEnumerable <XElement> filter;
                if (depot != null)
                {
                    filter = from w in xml.Elements("Element")
                             join AcDepot d in _depots on
                                 (string) w.Attribute("depot") equals d.Name
                                 where (string)w.Attribute("depot") == depot.Name
                             select w;
                }
                else
                {
                    filter = from w in xml.Elements("Element")
                             join AcDepot d in _depots on
                                 (string) w.Attribute("depot") equals d.Name
                             select w;
                }

                foreach (XElement e in filter)
                {
                    AcWorkspace ws = new AcWorkspace();
                    ws.Name     = (string)e.Attribute("Name");
                    ws.Hidden   = (e.Attribute("hidden") != null); // attribute hidden exists only when true
                    ws.Location = (string)e.Attribute("Loc");
                    ws.Storage  = (string)e.Attribute("Storage");
                    ws.Host     = (string)e.Attribute("Host");
                    ws.ID       = (int)e.Attribute("Stream");
                    string temp = (string)e.Attribute("depot");
                    ws.Depot       = _depots.getDepot(temp);
                    ws.TargetLevel = (int)e.Attribute("Target_trans");
                    ws.UpdateLevel = (int)e.Attribute("Trans");
                    ws.FileModTime = e.acxTime("fileModTime");
                    int type = (int)e.Attribute("Type");
                    ws.Type = (WsType)type;
                    int eol = (int)e.Attribute("EOL");
                    ws.EOL            = (WsEOL)eol;
                    ws.Principal.ID   = (int)e.Attribute("user_id");
                    ws.Principal.Name = (string)e.Attribute("user_name");
                    lock (_locker) { Add(ws); }
                }

                ret = true; // operation succeeded
            }

            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in AcWorkspaces.storeWSpaces{Environment.NewLine}{ecx.Message}");
            }

            return(ret);
        }
        /// <summary>
        /// Populate this container with AcProperty objects for all streams in \e depot or just \e stream in \e depot.
        /// Optionally include hidden (removed) streams.
        /// </summary>
        /// <param name="depot">The depot to query for properties.</param>
        /// <param name="stream">The stream for a specific stream only, otherwise \e null for all streams in \e depot.</param>
        /// <param name="includeHidden">\e true to include properties for hidden (removed) streams, \e false otherwise.</param>
        /// <returns>\e true if initialization succeeded, \e false otherwise.</returns>
        /// <exception cref="AcUtilsException">caught and [logged](@ref AcUtils#AcDebug#initAcLogging) in
        /// <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on \c getproperty command failure.</exception>
        /// <exception cref="Exception">caught and logged in same on failure to handle a range of exceptions.</exception>
        /*! \sa [AcProperties constructor](@ref AcUtils#AcProperties#AcProperties), initAsync(string, bool) */
        /*! \getproperty_ <tt>getproperty \<-fx | -fix\> \<-s \<stream\> | -ks -p \<depot\>\></tt> */
        public async Task <bool> initAsync(AcDepot depot, AcStream stream = null, bool includeHidden = false)
        {
            bool ret = false; // assume failure

            try
            {
                string cmd = null;
                if (stream != null && includeHidden)
                {
                    cmd = $@"getproperty -fix -s ""{stream}""";
                }
                else if (stream != null && !includeHidden)
                {
                    cmd = $@"getproperty -fx -s ""{stream}""";
                }
                else if (includeHidden) // request is for all streams in depot including those that are hidden
                {
                    cmd = $@"getproperty -fix -ks -p ""{depot}""";
                }
                else  // request is for all streams except those that are hidden
                {
                    cmd = $@"getproperty -fx -ks -p ""{depot}""";
                }

                AcResult r = await AcCommand.runAsync(cmd).ConfigureAwait(false);

                if (r != null && r.RetVal == 0)
                {
                    XElement xml = XElement.Parse(r.CmdResult);
                    foreach (XElement e in xml.Elements("property"))
                    {
                        AcProperty property = new AcProperty();
                        string     kind     = (string)e.Attribute("kind");
                        property.Kind      = (PropKind)Enum.Parse(typeof(PropKind), kind);
                        property.Depot     = depot;
                        property.ID        = (int)e.Attribute("streamNumber");
                        property.Name      = (string)e.Attribute("streamName");
                        property.PropName  = (string)e.Attribute("propertyName");
                        property.PropValue = (string)e;
                        lock (_locker) { Add(property); }
                    }

                    ret = true; // operation succeeded
                }
            }

            catch (AcUtilsException ecx)
            {
                AcDebug.Log($"AcUtilsException caught and logged in AcProperties.initAsync(AcDepot, AcStream, bool){Environment.NewLine}{ecx.Message}");
            }

            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in AcProperties.initAsync(AcDepot, AcStream, bool){Environment.NewLine}{ecx.Message}");
            }

            return(ret);
        }
Esempio n. 5
0
        /// <summary>
        /// Populate this container with AcLock objects on streams in \e depots.
        /// </summary>
        /// <param name="depots">Limit the list of locks to those on streams in \e depots only. Depot names in \e depots
        /// must match their respective AccuRev depot name exactly.</param>
        /// <returns>\e true if initialization succeeded, \e false otherwise.</returns>
        /// <exception cref="AcUtilsException">caught and [logged](@ref AcUtils#AcDebug#initAcLogging)
        /// in <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on \c show command failure.</exception>
        /// <exception cref="Exception">caught and logged in same on failure to handle a range of exceptions.</exception>
        /*! \sa [AcLocks constructor](@ref AcUtils#AcLocks#AcLocks), initAsync(AcDepot), initAsync(StreamsCollection) */
        /*! \show_ <tt>show -fx locks</tt> */
        public async Task <bool> initAsync(DepotsCollection depots)
        {
            AcDepots dlist = new AcDepots();

            if (!(await dlist.initAsync(depots).ConfigureAwait(false)))
            {
                return(false);
            }

            bool ret = false; // assume failure

            try
            {
                AcResult r = await AcCommand.runAsync("show -fx locks").ConfigureAwait(false);

                if (r != null && r.RetVal == 0)
                {
                    bool     result = true;
                    XElement xml    = XElement.Parse(r.CmdResult);
                    for (int ii = 0; ii < dlist.Count && result; ii++)
                    {
                        AcDepot depot = dlist[ii];
                        IEnumerable <XElement> query = from e in xml.Elements("Element")
                                                       join AcStream s in depot.Streams on(string) e.Attribute("Name") equals s.Name
                                                       select e;

                        result = initList(query);
                    }

                    ret = result;
                }
            }

            catch (AcUtilsException ecx)
            {
                AcDebug.Log($"AcUtilsException caught and logged in AcLocks.initAsync(DepotsCollection){Environment.NewLine}{ecx.Message}");
            }

            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in AcLocks.initAsync(DepotsCollection){Environment.NewLine}{ecx.Message}");
            }

            return(ret);
        }
        /// <summary>
        /// Populate this container with AcWorkspace objects as per constructor parameters.
        /// </summary>
        /// <remarks>The resultant list includes only workspaces from depots the user has [permission to access](@ref AcUtils#AcPermissions#AcPermissions).</remarks>
        /// <param name="depot">Specify a depot to limit the query to workspaces in \e depot only, not all depots in the repository.</param>
        /// <returns>\e true if initialization succeeded, \e false otherwise.</returns>
        /*! \sa [AcWorkspaces constructor](@ref AcUtils#AcWorkspaces#AcWorkspaces) */
        public async Task <bool> initAsync(AcDepot depot = null)
        {
            Task <AcResult> ws = getWorkspacesXMLAsync();

            if (_includeRefTrees)
            {
                // run both in parallel
                Task <AcResult> rt  = getReferenceTreesXMLAsync();
                AcResult[]      arr = await Task.WhenAll(ws, rt).ConfigureAwait(false);

                bool ret = (arr != null && arr.All(n => n != null && n.RetVal == 0)); // true if both were successful
                if (!ret)
                {
                    return(false);
                }
                foreach (AcResult r in arr)
                {
                    if (!storeWSpaces(r, depot))
                    {
                        return(false);
                    }
                }
            }
            else
            {
                AcResult r = await ws.ConfigureAwait(false);

                if (r != null && r.RetVal == 0)
                {
                    if (!storeWSpaces(r, depot))
                    {
                        return(false);
                    }
                }
                else
                {
                    return(false);
                }
            }

            return(true);
        }
Esempio n. 7
0
        /// <summary>
        /// Populate this container with AcLock objects on streams in \e depot or all AcLock objects in the repository.
        /// </summary>
        /// <param name="depot">Limit the list of locks to those on \e depot only, otherwise
        /// \e null for all locks in the repository.</param>
        /// <returns>\e true if initialization succeeded, \e false otherwise.</returns>
        /// <exception cref="AcUtilsException">caught and [logged](@ref AcUtils#AcDebug#initAcLogging)
        /// in <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on \c show command failure.</exception>
        /// <exception cref="Exception">caught and logged in same on failure to handle a range of exceptions.</exception>
        /*! \sa [AcLocks constructor](@ref AcUtils#AcLocks#AcLocks), initAsync(DepotsCollection), initAsync(StreamsCollection) */
        /*! \show_ <tt>show -fx locks</tt> */
        public async Task <bool> initAsync(AcDepot depot = null)
        {
            bool ret = false; // assume failure

            try
            {
                AcResult r = await AcCommand.runAsync("show -fx locks").ConfigureAwait(false);

                if (r != null && r.RetVal == 0)
                {
                    XElement xml = XElement.Parse(r.CmdResult);
                    IEnumerable <XElement> query = null;
                    if (depot == null)
                    {
                        query = from e in xml.Elements("Element")
                                select e;
                    }
                    else
                    {
                        query = from e in xml.Elements("Element")
                                join AcStream s in depot.Streams on(string) e.Attribute("Name") equals s.Name
                                select e;
                    }
                    ret = initList(query);
                }
            }

            catch (AcUtilsException ecx)
            {
                AcDebug.Log($"AcUtilsException caught and logged in AcLocks.initAsync(AcDepot){Environment.NewLine}{ecx.Message}");
            }

            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in AcLocks.initAsync(AcDepot){Environment.NewLine}{ecx.Message}");
            }

            return(ret);
        }
        /// <summary>
        /// For the real version specified in the argument list, get the real stream/version
        /// for the version in the specified version's workspace backing stream.
        /// </summary>
        /// <remarks>\c diff used here with the \c -i option, not to run a diff but to get the EID's only.</remarks>
        /// <param name="realverspec">Real version specification in text format,
        /// for example <tt>PG_MAINT1_barnyrd\4</tt> (numeric format won't work).</param>
        /// <param name="depot">The depot.</param>
        /// <param name="depotrelpath">Depot relative path, for example <tt>\\\\\.\\Bin\\foo.java</tt></param>
        /// <returns>An array initialized as <em>int[]={realStreamNumber, realVersionNumber}</em> if available, otherwise \e null.</returns>

        /*! \diff_ The \c diff command returns <em>zero (0)</em> for no differences found,
         *  <em>one (1)</em> for differences found, or <em>two (2)</em> on \c diff program error.<br>
         *  <tt>diff -fx -v \<realverspec\> -b -i -p \<depot\> \<depotrelpath\></tt> */
        /*! > -v -b Compare the specified version (X) with the version in X's stream's backing stream. (Use -v -V instead if comparing elements of type text that are active in a time-based stream.) */
        /*! > -i Information only: Report the IDs of the two versions, but don't actually compare them. This option is valid only in a command that uses a -v/-V combination or a -v/-b combination. If an element appears only in the workspace stream, not in the workspace's backing stream, this option does not list it. */
        /// <exception cref="AcUtilsException">caught and [logged](@ref AcUtils#AcDebug#initAcLogging)
        /// in <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on \c diff command failure.</exception>
        /// <exception cref="Exception">caught and logged in same on failure to handle a range of exceptions.</exception>
        public static async Task <int[]> getBackedVersionAsync(string realverspec, AcDepot depot, string depotrelpath)
        {
            int[] arr = null; // {realStreamNumber, realVersionNumber}
            try
            {
                AcResult r = await AcCommand.runAsync($@"diff -fx -v ""{realverspec}"" -b -i -p ""{depot}"" ""{depotrelpath}""")
                             .ConfigureAwait(false);

                if (r != null && r.RetVal < 2)
                {
                    XElement xml     = XElement.Parse(r.CmdResult);
                    XElement stream2 = xml.Descendants().Where(n => n.Name == "Stream2").SingleOrDefault();
                    if (stream2 != null)
                    {
                        XAttribute version = stream2.Attributes("Version").SingleOrDefault();
                        if (version != null)
                        {
                            string   temp              = (string)version;
                            string[] a                 = temp.Split('/');
                            int      realStreamNumber  = Int32.Parse(a[0], NumberStyles.Integer);
                            int      realVersionNumber = Int32.Parse(a[1], NumberStyles.Integer);
                            arr = new int[] { realStreamNumber, realVersionNumber };
                        }
                    }
                }
            }

            catch (AcUtilsException ecx)
            {
                AcDebug.Log($"AcUtilsException caught and logged in AcQuery.getBackedVersionAsync{Environment.NewLine}{ecx.Message}");
            }

            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in AcQuery.getBackedVersionAsync{Environment.NewLine}{ecx.Message}");
            }

            return(arr);
        }
        /// <summary>
        /// Populate this container with AcRule objects for all streams in \e depot
        /// as per [constructor parameter](@ref AcUtils#AcRules#AcRules) \e explicitOnly.
        /// </summary>
        /// <param name="depot">All streams in \e depot to query for rules.</param>
        /// <param name="progress">Optionally report progress back to the caller.</param>
        /// <returns>\e true if no failure occurred and list was initialized successfully, \e false otherwise.</returns>
        /// <exception cref="Exception">caught and [logged](@ref AcUtils#AcDebug#initAcLogging) in
        /// <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on failure to handle a range of exceptions.</exception>
        public async Task <bool> initAsync(AcDepot depot, IProgress <int> progress = null)
        {
            bool ret = false; // assume failure

            try
            {
                int num = depot.Streams.Count();
                List <Task <bool> >      tasks = new List <Task <bool> >(num);
                Func <Task <bool>, bool> cf    = t =>
                {
                    bool res = t.Result;
                    if (res && progress != null)
                    {
                        progress.Report(Interlocked.Increment(ref _counter));
                    }
                    return(res);
                };

                foreach (AcStream stream in depot.Streams)
                {
                    Task <bool> t = initAsync(stream).ContinueWith(cf);
                    tasks.Add(t);
                }

                bool[] arr = await Task.WhenAll(tasks).ConfigureAwait(false);

                ret = (arr != null && arr.All(n => n == true)); // true if all succeeded
            }

            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in AcRules.initAsync(AcDepot){Environment.NewLine}{ecx.Message}");
            }

            return(ret);
        }
 /// <summary>
 /// Get the AcWorkspace object in \e depot with workspace \e ID number.
 /// </summary>
 /// <param name="depot">Depot where the workspace resides.</param>
 /// <param name="ID">Workspace ID number to query.</param>
 /// <returns>AcWorkspace object otherwise \e null if not found.</returns>
 public AcWorkspace getWorkspace(AcDepot depot, int ID)
 {
     return(this.SingleOrDefault(n => n.ID == ID && n.Depot.Equals(depot)));
 }