Example #1
0
        public override Task <IList <TableResult> > ExecuteMirrorBatchAsync(
            TableBatchOperation originalBatch, IList <TableResult> originalResponse,
            TableRequestOptions requestOptions = null, OperationContext operationContext = null)
        {
            ChainTableUtils.GetBatchPartitionKey(originalBatch);  // For validation; result ignored

            // Copy the table.  Entities are aliased to the original table, so don't mutate them.
            var tmpTable    = new SortedDictionary <PrimaryKey, DynamicTableEntity>(table);
            int tmpNextEtag = nextEtag;
            var results     = new List <TableResult>();

            for (int i = 0; i < originalBatch.Count; i++)
            {
                TableOperation     op           = originalBatch[i];
                TableOperationType opType       = op.GetOperationType();
                ITableEntity       passedEntity = op.GetEntity();
                PrimaryKey         key          = passedEntity.GetPrimaryKey();
                DynamicTableEntity oldEntity    = tmpTable.GetValueOrDefault(key);

                DynamicTableEntity newEntity  = null;
                HttpStatusCode     statusCode = HttpStatusCode.NoContent;

                if (opType == TableOperationType.Insert)
                {
                    if (oldEntity != null)
                    {
                        throw ChainTableUtils.GenerateBatchException(HttpStatusCode.Conflict, i);
                    }
                    else
                    {
                        newEntity  = ChainTableUtils.CopyEntity <DynamicTableEntity>(passedEntity);
                        statusCode = HttpStatusCode.Created;
                    }
                }
                else if (opType == TableOperationType.InsertOrReplace)
                {
                    newEntity = ChainTableUtils.CopyEntity <DynamicTableEntity>(passedEntity);
                }
                else if (opType == TableOperationType.InsertOrMerge)
                {
                    if (oldEntity == null)
                    {
                        newEntity = ChainTableUtils.CopyEntity <DynamicTableEntity>(passedEntity);
                    }
                    else
                    {
                        newEntity = ChainTableUtils.CopyEntity <DynamicTableEntity>(oldEntity);
                        Merge(newEntity, passedEntity);
                    }
                }
                else if (opType == TableOperationType.Delete &&
                         passedEntity.ETag == ChainTable2Constants.ETAG_DELETE_IF_EXISTS)
                {
                    tmpTable.Remove(key);
                }
                else if (oldEntity == null)
                {
                    throw ChainTableUtils.GenerateBatchException(HttpStatusCode.NotFound, i);
                }
                else if (string.IsNullOrEmpty(passedEntity.ETag))
                {
                    // Enforce this because real Azure table will.
                    // XXX Ideally do this up front.
                    throw new ArgumentException(string.Format("Operation {0} requires an explicit ETag.", i));
                }
                else if (passedEntity.ETag != ChainTable2Constants.ETAG_ANY &&
                         oldEntity.ETag != passedEntity.ETag)
                {
                    throw ChainTableUtils.GenerateBatchException(HttpStatusCode.PreconditionFailed, i);
                }
                else if (opType == TableOperationType.Delete)
                {
                    tmpTable.Remove(key);
                }
                else if (opType == TableOperationType.Replace)
                {
                    newEntity = ChainTableUtils.CopyEntity <DynamicTableEntity>(passedEntity);
                }
                else if (opType == TableOperationType.Merge)
                {
                    newEntity = ChainTableUtils.CopyEntity <DynamicTableEntity>(oldEntity);
                    Merge(newEntity, passedEntity);
                }
                else
                {
                    // IChainTable2 does not allow Retrieve in a batch.
                    throw new NotImplementedException();
                }

                if (newEntity != null)
                {
                    newEntity.ETag      = (originalResponse != null) ? originalResponse[i].Etag : (tmpNextEtag++).ToString();
                    newEntity.Timestamp = DateTimeOffset.MinValue;  // Arbitrary, deterministic
                    tmpTable[key]       = newEntity;
                }
                results.Add(new TableResult {
                    Result         = passedEntity,
                    HttpStatusCode = (int)statusCode,
                    Etag           = (newEntity != null) ? newEntity.ETag : null,
                });
            }

            // If we got here, commit.
            table    = tmpTable;
            nextEtag = tmpNextEtag;
            for (int i = 0; i < originalBatch.Count; i++)
            {
                if (results[i].Etag != null)  // not delete
                {
                    originalBatch[i].GetEntity().ETag = results[i].Etag;
                }
            }
            return(Task.FromResult((IList <TableResult>)results));
        }