Ejemplo n.º 1
0
        private async Task CommitEditsInternal(DbConnection connection, Func <Task> successHandler, Func <Exception, Task> errorHandler)
        {
            try
            {
                // @TODO: Add support for transactional commits

                // Trust the RowEdit to sort itself appropriately
                var editOperations = EditCache.Values.ToList();
                editOperations.Sort();
                foreach (var editOperation in editOperations)
                {
                    // Get the command from the edit operation and execute it
                    using (DbCommand editCommand = editOperation.GetCommand(connection))
                        using (DbDataReader reader = await editCommand.ExecuteReaderAsync())
                        {
                            // Apply the changes of the command to the result set
                            await editOperation.ApplyChanges(reader);
                        }

                    // If we succeeded in applying the changes, then remove this from the cache
                    // @TODO: Prevent edit sessions from being modified while a commit is in progress
                    RowEditBase re;
                    EditCache.TryRemove(editOperation.RowId, out re);
                }
                await successHandler();
            }
            catch (Exception e)
            {
                await errorHandler(e);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Creates a new row update and adds it to the update cache
        /// </summary>
        /// <exception cref="InvalidOperationException">If inserting into cache fails</exception>
        /// <returns>The internal ID of the newly created row</returns>
        public EditCreateRowResult CreateRow()
        {
            ThrowIfNotInitialized();

            // Create a new row ID (atomically, since this could be accesses concurrently)
            long newRowId = NextRowId++;

            // Create a new row create update and add to the update cache
            RowCreate newRow = new RowCreate(newRowId, associatedResultSet, objectMetadata);

            if (!EditCache.TryAdd(newRowId, newRow))
            {
                // Revert the next row ID
                NextRowId--;
                throw new InvalidOperationException(SR.EditDataFailedAddRow);
            }

            EditCreateRowResult output = new EditCreateRowResult
            {
                NewRowId      = newRow.RowId,
                DefaultValues = newRow.DefaultValues
            };

            return(output);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Removes a pending row update from the update cache.
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If a pending row update with the given row ID does not exist.
        /// </exception>
        /// <param name="rowId">The internal ID of the row to reset</param>
        public void RevertRow(long rowId)
        {
            ThrowIfNotInitialized();

            // Attempt to remove the row with the given ID
            RowEditBase removedEdit;

            if (!EditCache.TryRemove(rowId, out removedEdit))
            {
                throw new ArgumentOutOfRangeException(nameof(rowId), SR.EditDataUpdateNotPending);
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Removes the edit from the edit cache if the row is no longer dirty
        /// </summary>
        /// <param name="rowId">ID of the update to cleanup</param>
        /// <param name="editCellResult">Result with row dirty flag</param>
        private void CleanupEditIfRowClean(long rowId, EditCellResult editCellResult)
        {
            // If the row is still dirty, don't do anything
            if (editCellResult.IsRowDirty)
            {
                return;
            }

            // Make an attempt to remove the clean row edit. If this fails, it'll be handled on commit attempt.
            RowEditBase removedRow;

            EditCache.TryRemove(rowId, out removedRow);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Retrieves a subset of rows with the pending updates applied. If more rows than exist
        /// are requested, only the rows that exist will be returned.
        /// </summary>
        /// <param name="startIndex">Index to start returning rows from</param>
        /// <param name="rowCount">The number of rows to return.</param>
        /// <returns>An array of rows with pending edits applied</returns>
        public async Task <EditRow[]> GetRows(long startIndex, int rowCount)
        {
            ThrowIfNotInitialized();

            // Get the cached rows from the result set
            ResultSetSubset cachedRows = startIndex < associatedResultSet.RowCount
                ? await associatedResultSet.GetSubset(startIndex, rowCount)
                : new ResultSetSubset
            {
                RowCount = 0,
                Rows     = new DbCellValue[][] { }
            };

            // Convert the rows into EditRows and apply the changes we have
            List <EditRow> editRows = new List <EditRow>();

            for (int i = 0; i < cachedRows.RowCount; i++)
            {
                long        rowId = i + startIndex;
                RowEditBase edr;
                if (EditCache.TryGetValue(rowId, out edr))
                {
                    // Ask the edit object to generate an edit row
                    editRows.Add(edr.GetEditRow(cachedRows.Rows[i]));
                }
                else
                {
                    // Package up the existing row into a clean edit row
                    EditRow er = new EditRow
                    {
                        Id    = rowId,
                        Cells = cachedRows.Rows[i].Select(cell => new EditCell(cell, false)).ToArray(),
                        State = EditRow.EditRowState.Clean
                    };
                    editRows.Add(er);
                }
            }

            // If the requested range of rows was at the end of the original cell set and we have
            // added new rows, we need to reflect those changes
            if (rowCount > cachedRows.RowCount)
            {
                long endIndex = startIndex + cachedRows.RowCount;
                var  newRows  = EditCache.Where(edit => edit.Key >= endIndex).Take(rowCount - cachedRows.RowCount);
                editRows.AddRange(newRows.Select(newRow => newRow.Value.GetEditRow(null)));
            }

            return(editRows.ToArray());
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Creates a new row update and adds it to the update cache
        /// </summary>
        /// <exception cref="InvalidOperationException">If inserting into cache fails</exception>
        /// <returns>The internal ID of the newly created row</returns>
        public EditCreateRowResult CreateRow()
        {
            ThrowIfNotInitialized();

            // Create a new row ID (atomically, since this could be accesses concurrently)
            long newRowId = NextRowId++;

            // Create a new row create update and add to the update cache
            RowCreate newRow = new RowCreate(newRowId, associatedResultSet, objectMetadata);

            if (!EditCache.TryAdd(newRowId, newRow))
            {
                // Revert the next row ID
                NextRowId--;
                throw new InvalidOperationException(SR.EditDataFailedAddRow);
            }

            // Set the default values of the row if we know them
            string[] defaultValues = new string[objectMetadata.Columns.Length];
            for (int i = 0; i < objectMetadata.Columns.Length; i++)
            {
                EditColumnMetadata col = objectMetadata.Columns[i];

                // If the column is calculated, return the calculated placeholder as the display value
                if (col.IsCalculated.HasTrue())
                {
                    defaultValues[i] = SR.EditDataComputedColumnPlaceholder;
                }
                else
                {
                    if (col.DefaultValue != null)
                    {
                        newRow.SetCell(i, col.DefaultValue);
                    }
                    defaultValues[i] = col.DefaultValue;
                }
            }

            EditCreateRowResult output = new EditCreateRowResult
            {
                NewRowId      = newRowId,
                DefaultValues = defaultValues
            };

            return(output);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Creates a delete row update and adds it to the update cache
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// If row requested to delete already has a pending change in the cache
        /// </exception>
        /// <param name="rowId">The internal ID of the row to delete</param>
        public void DeleteRow(long rowId)
        {
            ThrowIfNotInitialized();

            // Sanity check the row ID
            if (rowId >= NextRowId || rowId < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(rowId), SR.EditDataRowOutOfRange);
            }

            // Create a new row delete update and add to cache
            RowDelete deleteRow = new RowDelete(rowId, associatedResultSet, objectMetadata);

            if (!EditCache.TryAdd(rowId, deleteRow))
            {
                throw new InvalidOperationException(SR.EditDataUpdatePending);
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Reverts a cell in a pending edit
        /// </summary>
        /// <param name="rowId">Internal ID of the row to have its edits reverted</param>
        /// <param name="columnId">Ordinal ID of the column to revert</param>
        /// <returns>String version of the old value for the cell</returns>
        public EditRevertCellResult RevertCell(long rowId, int columnId)
        {
            ThrowIfNotInitialized();

            // Attempt to get the row edit with the given ID
            RowEditBase pendingEdit;

            if (!EditCache.TryGetValue(rowId, out pendingEdit))
            {
                throw new ArgumentOutOfRangeException(nameof(rowId), SR.EditDataUpdateNotPending);
            }

            // Update the row
            EditRevertCellResult revertResult = pendingEdit.RevertCell(columnId);

            CleanupEditIfRowClean(rowId, revertResult);

            // Have the edit base revert the cell
            return(revertResult);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Performs an update to a specific cell in a row. If the row has not already been
        /// initialized with a record in the update cache, one is created.
        /// </summary>
        /// <exception cref="InvalidOperationException">If adding a new update row fails</exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// If the row that is requested to be edited is beyond the rows in the results and the
        /// rows that are being added.
        /// </exception>
        /// <param name="rowId">The internal ID of the row to edit</param>
        /// <param name="columnId">The ordinal of the column to edit in the row</param>
        /// <param name="newValue">The new string value of the cell to update</param>
        public EditUpdateCellResult UpdateCell(long rowId, int columnId, string newValue)
        {
            ThrowIfNotInitialized();

            // Sanity check to make sure that the row ID is in the range of possible values
            if (rowId >= NextRowId || rowId < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(rowId), SR.EditDataRowOutOfRange);
            }

            // Attempt to get the row that is being edited, create a new update object if one
            // doesn't exist
            // NOTE: This *must* be done as a lambda. RowUpdate creation requires that the row
            // exist in the result set. We only want a new RowUpdate to be created if the edit
            // doesn't already exist in the cache
            RowEditBase editRow = EditCache.GetOrAdd(rowId, key => new RowUpdate(rowId, associatedResultSet, objectMetadata));

            // Update the row
            EditUpdateCellResult result = editRow.SetCell(columnId, newValue);

            CleanupEditIfRowClean(rowId, result);

            return(result);
        }