Ejemplo n.º 1
0
        /// <inheritdoc />
        public ICommenceQueryRowSet GetQueryRowSetByID(string pRowID, CmcOptionFlags flags = CmcOptionFlags.Default)
        {
            ICommenceQueryRowSet qrs = null;

            try
            {
                qrs = new CommenceQueryRowSet(_cur, pRowID, _rcwReleasePublisher, flags);
            }
            catch (COMException e)
            {
                throw new CommenceCOMException("Unable to get a QueryRowSet object from Commence", e);
            }
            return(qrs);
        }
Ejemplo n.º 2
0
        /// <inheritdoc />
        // only works on shared databases; local databases have no thids
        // you cannot just pass a thid value you retrieved by GetRowID(), you must strip off the category (=first) sequence
        // e.g. if GetRowID returns 0C:80006901:94BD3402, you must strip off the leading 0C: part
        public ICommenceQueryRowSet GetQueryRowSetByThid(string pThid, CmcOptionFlags flags = CmcOptionFlags.UseThids)
        {
            ICommenceQueryRowSet qrs = null;

            try
            {
                qrs = new CommenceQueryRowSet(_cur, pThid, _rcwReleasePublisher, flags, RowSetIdentifier.Thid);
            }
            catch (COMException e)
            {
                throw new CommenceCOMException("Unable to get a QueryRowSet object from Commence", e);
            }
            return(qrs);
        }
Ejemplo n.º 3
0
        /// <inheritdoc />
        public ICommenceQueryRowSet GetQueryRowSet(int nRows, CmcOptionFlags flags = CmcOptionFlags.Default)
        {
            ICommenceQueryRowSet qrs = null;

            try
            {
                qrs = new CommenceQueryRowSet(_cur, nRows, _rcwReleasePublisher, flags);
            }
            catch (COMException e)
            {
                // when very large amounts of data are requested, this may cause a "Couldn't get memory" error in Commence.
                throw new CommenceCOMException("Unable to get a QueryRowSet object from Commence", e);
            }
            return(qrs);
        }
Ejemplo n.º 4
0
        /// <inheritdoc />
        public bool HasDuplicates(string columnName, bool caseSensitive = true)
        {
            if (this.RowCount == 0)
            {
                return(false);
            }                                         // nothing to compare

            // we could just filter the cursor, but that is tricky for a couple of reasons:
            // - cursor may already be filtered, adding a new is not straightforward due to number and logic.
            // - filtering would require we obtain the fieldtype, because different fieldtypes take different qualifiers.
            // therefore we will retrieve all values and compare them
            // this is a little slower of course.
            List <string> values = new List <string>();
            // capture current row by moving rowpointer to start.
            // currentrow will contain the number of rows moved back (for instance -789)
            int currentrow = this.SeekRow(CmcCursorBookmark.Beginning, 0);
            int colindex   = -1;

            for (int i = 0; i < this.RowCount; i += 100) // process 100 rows at a time. A row can hold 250 columns of 30.000 characters each; 100 is an arbitrary amount.
            {
                using (ICommenceQueryRowSet qrs = this.GetQueryRowSet(100))
                {
                    colindex = qrs.GetColumnIndex(columnName);
                    if (colindex == -1) // still -1?
                    {
                        throw new CommenceCOMException("Column '" + columnName + "' is not included in cursor.");
                    }
                    for (int j = 0; j < qrs.RowCount; j++)
                    {
                        values.Add(qrs.GetRow(j)[colindex]);
                    }
                }
            }
            // put rowpointer back where it started
            this.SeekRow(CmcCursorBookmark.Beginning, Math.Abs(currentrow)); // reset rowpointer
            List <string> distinctvalues = caseSensitive
                ? values.Distinct().ToList()
                : values.Distinct(StringComparer.CurrentCultureIgnoreCase).ToList();

            return(values.Count() != distinctvalues.Count());
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Reads the Commence data (using API, not DDE). This is the fastest way to retrieve Commence data.
        /// </summary>
        /// <param name="nRows">Number of rows to read at a time.</param>
        /// <returns>string[][] (a 'jagged array').</returns>
        /// <remarks>This function has no knowledge of the rowpointer.</remarks>
        /// <exception cref="CommenceCOMException"></exception>
        internal string[][] GetRawData(int nRows)
        {
            /* Note that for connected items, Commence returns a linefeed-delimited string, OR a comma delimited string(!)
             * If the connected field has no data, an empty string is returned, again linefeed-delimited.
             * It is up to the consumer to deal with this.
             * The Headers property can be used to determine what fieldtype is being returned.
             *
             * Also note, that by getting a RowSet, Commence automatically advances the cursor's rowpointer for us
             * This can lead to some confusion for those who are used to advance it manually, like in ADO.
             */

            // CommenceQueryRowSet implements IDisposable, so we can use the using directive
            // This is vitally important, because it's Dispose method releases the COM reference (CCW) to Commence's QueryRowSet
            // Without releasing the CCW, all data would be held in memory until I don't know when exactly and memory usage would skyrocket.
            // By implementing it like this we make sure that the Garbage Collector keeps memory in check,
            // and we don't have to worry about releasing RCWs.
            // However: we still run into issues if the garbage collector kicks in *after* Commence runs out of memory
            // Commence is pretty finicky about that. This is why an explicit close is included.
            using (ICommenceQueryRowSet qrs = this.GetQueryRowSet(nRows))
            {
                // number of rows requested may be larger than number of available rows in rowset,
                // so make sure the return value is sized properly
                string[][] rowvalues  = new string[qrs.RowCount][];
                string[]   buffer     = null;
                int        numColumns = qrs.ColumnCount; // store number of columns so we only need 1 COM call; makes method much faster
                // I don't think this adds anything
                // Not calling this may result in a performance gain for small nRows,
                // because it saves a COM call.
                // int rowpointer = this.SeekRow(CmcCursorBookmark.Current, 0); // determine the rowpointer we are currently at
                int numRows = qrs.RowCount; // store number of rows to be read so we need only 1 COM call
                for (int i = 0; i < numRows; i++)
                {
                    // create room for number of columns plus one extra to hold the thid
                    // this means that it is up to the consumers to pick the right columns!
                    rowvalues[i] = new string[numColumns + 1];
                    if (this.Flags.HasFlag(CmcOptionFlags.UseThids))           // do not make the extra API call unless requested
                    {
                        string thid = qrs.GetRowID(i, CmcOptionFlags.Default); // GetRowID does not advance the rowpointer. Note that the flag must be 0.
                        rowvalues[i][0] = thid;                                // put thid in first column of row
                    }

                    buffer = qrs.GetRow(i); // don't bother with canonical flag, it doesn't work properly anyway.
                    if (buffer == null)
                    {
                        qrs.Close();
                        // This will throw an error containing the row the error occurred at,
                        // but I do not think it adds anything. To an end user, it means nothing.
                        // Within the assembly, I have no idea how it would be helpful;
                        // you could possibly derive it from within the method calling this anyway
                        //throw new CommenceCOMException("An error occurred while reading row" + (rowpointer + i).ToString());
                        // just throw an error with info on the QueryRowSet, which may still be overkill.
                        throw new CommenceCOMException("An error occurred while reading QueryRowSet row" + i);
                    }
                    for (int j = 0; j < numColumns; j++)
                    {
                        rowvalues[i][j + 1] = buffer[j]; // put rowvalue in 2nd and up column of row
                    } // j
                } // i
                qrs.Close(); // close COM reference explicitly. the 'using' directive will do this for us, but GC may not kick in in time.
                return(rowvalues);
            } // using; qrs will be disposed now
        }