/// <summary>
        /// Executes the operation.
        /// </summary>
        /// <param name="batchReader">The batch reader.</param>
        /// <param name="batchId">The batch id.</param>
        /// <param name="originalRequest">The original request containing all the batch requests.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The response for the operation.</returns>
        public virtual async Task<ODataBatchResponseItem> ExecuteOperationAsync(ODataBatchReader batchReader, Guid batchId, HttpRequestMessage originalRequest, CancellationToken cancellationToken)
        {
            if (batchReader == null)
            {
                throw Error.ArgumentNull("batchReader");
            }
            if (originalRequest == null)
            {
                throw Error.ArgumentNull("originalRequest");
            }

            cancellationToken.ThrowIfCancellationRequested();

            OperationRequestItem operation = new OperationRequestItem(await batchReader.ReadOperationRequestAsync(batchId, bufferContentStream: false));
            try
            {
                ODataBatchResponseItem response = await operation.SendRequestAsync(Invoker, cancellationToken);
                return response;
            }
            finally
            {
                originalRequest.RegisterForDispose(operation.GetResourcesForDisposal());
                originalRequest.RegisterForDispose(operation);
            }
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="batchReader">The reader to wrap.</param>
        /// <param name="testConfiguration">The test configuration to use.</param>
        public ODataBatchReaderTestWrapper(ODataBatchReader batchReader, ReaderTestConfiguration testConfiguration)
        {
            ExceptionUtilities.CheckArgumentNotNull(batchReader, "batchReader");
            ExceptionUtilities.CheckArgumentNotNull(testConfiguration, "testConfiguration");

            this.batchReader = batchReader;
            this.testConfiguration = testConfiguration;
        }
示例#3
0
        private Exception ProcessCurrentOperationResponse(ODataBatchReader batchReader, bool isChangesetOperation)
        {
            Debug.Assert(batchReader != null, "batchReader != null");
            Debug.Assert(batchReader.State == ODataBatchReaderState.Operation, "This method requires the batch reader to be on an operation.");

            ODataBatchOperationResponseMessage operationResponseMessage = batchReader.CreateOperationResponseMessage();
            Descriptor descriptor = null;

            if (isChangesetOperation)
            {
                // We need to peek at the content-Id before handing the response to the user, so we can expose the Descriptor them.
                // We're OK with this exception to our general rule of not using them before ReceivingResponse event is fired.
                this.entryIndex = this.ValidateContentID(operationResponseMessage.ContentId);
                descriptor = this.ChangedEntries[entryIndex];
            }

            // If we hit en error inside a batch, we will never expose a descriptor since we don't know which one to return.
            // The descriptor we fetched above based on the content-ID is bogus because the server returns an errounous content-id when
            // it hits an error inside batch.
            if (!WebUtil.SuccessStatusCode((HttpStatusCode)operationResponseMessage.StatusCode))
            {
                descriptor = null;
            }

            this.RequestInfo.Context.FireReceivingResponseEvent(new ReceivingResponseEventArgs(operationResponseMessage, descriptor, true));

            // We need to know if the content of the operation response is empty or not.
            // We also need to cache the entire content, since in case of GET response the response itself will be parsed
            // lazily and so it can happen that we will move the batch reader after this operation before we actually read
            // the content of the operation.
            Stream originalOperationResponseContentStream = operationResponseMessage.GetStream();
            if (originalOperationResponseContentStream == null)
            {
                Error.ThrowBatchExpectedResponse(InternalError.NullResponseStream);
            }

            MemoryStream operationResponseContentStream;
            try
            {
                operationResponseContentStream = new MemoryStream();
                WebUtil.CopyStream(originalOperationResponseContentStream, operationResponseContentStream, ref this.streamCopyBuffer);
                operationResponseContentStream.Position = 0;
            }
            finally
            {
                originalOperationResponseContentStream.Dispose();
            }

            this.currentOperationResponse = new CurrentOperationResponse(
                (HttpStatusCode)operationResponseMessage.StatusCode,
                operationResponseMessage.Headers,
                operationResponseContentStream);

            Version responseVersion;
            string headerName = XmlConstants.HttpODataVersion;
            return BaseSaveResult.HandleResponse(
                this.RequestInfo,
                this.currentOperationResponse.StatusCode,
                this.currentOperationResponse.Headers.GetHeader(headerName),
                () => this.currentOperationResponse.ContentStream,
                false,
                out responseVersion);
        }
示例#4
0
        /// <summary>
        /// process the batch response
        /// </summary>
        /// <param name="batchReader">The batch reader to use for reading the batch response.</param>
        /// <returns>enumerable of QueryResponse or null</returns>
        /// <remarks>
        /// The batch message reader for the entire batch response is stored in this.batchMessageReader.
        /// Note that this method takes over the ownership of this reader and must Dispose it if it successfully returns.
        /// </remarks>
        private IEnumerable<OperationResponse> HandleBatchResponse(ODataBatchReader batchReader)
        {
            try
            {
                if (this.batchMessageReader == null)
                {
                    // The enumerable returned by this method can be enumerated multiple times.
                    // In that case it looks like if the method is called multiple times.
                    // This didn't fail in previous versions, it simply returned no results, so we need to do the same.
                    yield break;
                }

                Debug.Assert(batchReader != null, "batchReader != null");

                bool changesetFound = false;
                bool insideChangeset = false;
                int queryCount = 0;
                int operationCount = 0;
                this.entryIndex = 0;
                while (batchReader.Read())
                {
                    switch (batchReader.State)
                    {
                        #region ChangesetStart
                        case ODataBatchReaderState.ChangesetStart:
                            if ((Util.IsBatchWithSingleChangeset(this.Options) && changesetFound) || (operationCount != 0))
                            {
                                // Throw if we encounter multiple changesets when running in batch with single changeset mode 
                                // or if we encounter operations outside of a changeset.
                                Error.ThrowBatchUnexpectedContent(InternalError.UnexpectedBeginChangeSet);
                            }

                            insideChangeset = true;
                            break;
                        #endregion

                        #region ChangesetEnd
                        case ODataBatchReaderState.ChangesetEnd:
                            changesetFound = true;
                            operationCount = 0;
                            insideChangeset = false;
                            break;
                        #endregion

                        #region Operation
                        case ODataBatchReaderState.Operation:
                            Exception exception = this.ProcessCurrentOperationResponse(batchReader, insideChangeset);
                            if (!insideChangeset)
                            {
                                #region Get response
                                Debug.Assert(operationCount == 0, "missing an EndChangeSet 2");

                                QueryOperationResponse qresponse = null;
                                try
                                {
                                    if (exception == null)
                                    {
                                        DataServiceRequest query = this.Queries[queryCount];
                                        ResponseInfo responseInfo = this.RequestInfo.GetDeserializationInfo(null /*mergeOption*/);
                                        MaterializeAtom materializer = DataServiceRequest.Materialize(
                                            responseInfo,
                                            query.QueryComponents(this.RequestInfo.Model),
                                            null,
                                            this.currentOperationResponse.Headers.GetHeader(XmlConstants.HttpContentType),
                                            this.currentOperationResponse.CreateResponseMessage(),
                                            query.PayloadKind);
                                        qresponse = QueryOperationResponse.GetInstance(query.ElementType, this.currentOperationResponse.Headers, query, materializer);
                                    }
                                }
                                catch (ArgumentException e)
                                {
                                    exception = e;
                                }
                                catch (FormatException e)
                                {
                                    exception = e;
                                }
                                catch (InvalidOperationException e)
                                {
                                    exception = e;
                                }

                                if (qresponse == null)
                                {
                                    if (this.Queries != null)
                                    {
                                        // this is the normal ExecuteBatch response
                                        DataServiceRequest query = this.Queries[queryCount];

                                        if (this.RequestInfo.IgnoreResourceNotFoundException && this.currentOperationResponse.StatusCode == HttpStatusCode.NotFound)
                                        {
                                            qresponse = QueryOperationResponse.GetInstance(query.ElementType, this.currentOperationResponse.Headers, query, MaterializeAtom.EmptyResults);
                                        }
                                        else
                                        {
                                            qresponse = QueryOperationResponse.GetInstance(query.ElementType, this.currentOperationResponse.Headers, query, MaterializeAtom.EmptyResults);
                                            qresponse.Error = exception;
                                        }
                                    }
                                    else
                                    {
                                        // This is top-level failure for SaveChanges(SaveChangesOptions.BatchWithSingleChangeset) or SaveChanges(SaveChangesOptions.BatchWithIndependentOperations) operations.
                                        // example: server doesn't support batching or number of batch objects exceeded an allowed limit.
                                        // ex could be null if the server responded to SaveChanges with an unexpected success with
                                        // response of batched GETS that did not correspond the original POST/PATCH/PUT/DELETE requests.
                                        // we expect non-null since server should have failed with a non-success code
                                        // and HandleResponse(status, ...) should generate the exception object
                                        throw exception;
                                    }
                                }

                                qresponse.StatusCode = (int)this.currentOperationResponse.StatusCode;
                                queryCount++;
                                yield return qresponse;
                                #endregion
                            }
                            else
                            {
                                #region Update response
                                try
                                {
                                    Descriptor descriptor = this.ChangedEntries[this.entryIndex];
                                    operationCount += this.SaveResultProcessed(descriptor);

                                    if (exception != null)
                                    {
                                        throw exception;
                                    }

                                    this.HandleOperationResponseHeaders(this.currentOperationResponse.StatusCode, this.currentOperationResponse.Headers);
#if DEBUG
                                    this.HandleOperationResponse(descriptor, this.currentOperationResponse.Headers, this.currentOperationResponse.StatusCode);
#else
                                    this.HandleOperationResponse(descriptor, this.currentOperationResponse.Headers);
#endif
                                }
                                catch (Exception e)
                                {
                                    this.ChangedEntries[this.entryIndex].SaveError = e;
                                    exception = e;

                                    if (!CommonUtil.IsCatchableExceptionType(e))
                                    {
                                        throw;
                                    }
                                }

                                ChangeOperationResponse changeOperationResponse =
                                    new ChangeOperationResponse(this.currentOperationResponse.Headers, this.ChangedEntries[this.entryIndex]);
                                changeOperationResponse.StatusCode = (int)this.currentOperationResponse.StatusCode;
                                if (exception != null)
                                {
                                    changeOperationResponse.Error = exception;
                                }

                                operationCount++;
                                this.entryIndex++;
                                yield return changeOperationResponse;
                                #endregion
                            }

                            break;
                        #endregion

                        default:
                            Error.ThrowBatchExpectedResponse(InternalError.UnexpectedBatchState);
                            break;
                    }
                }

                Debug.Assert(batchReader.State == ODataBatchReaderState.Completed, "unexpected batch state");

                // Check for a changeset without response (first line) or GET request without response (second line).
                // either all saved entries must be processed or it was a batch and one of the entries has the error
                if ((this.Queries == null &&
                    (!changesetFound ||
                     0 < queryCount ||
                     this.ChangedEntries.Any(o => o.ContentGeneratedForSave && o.SaveResultWasProcessed == 0) &&
                     (!this.IsBatchRequest || this.ChangedEntries.FirstOrDefault(o => o.SaveError != null) == null))) ||
                    (this.Queries != null && queryCount != this.Queries.Length))
                {
                    throw Error.InvalidOperation(Strings.Batch_IncompleteResponseCount);
                }
            }
            finally
            {
                // Note that this will be called only once the enumeration of all responses is finished and the Dispose
                // was called on the IEnumerator used for that enumeration. It is not called when the method returns,
                // since the compiler change this method to return the compiler-generated IEnumerable.
                Util.Dispose(ref this.batchMessageReader);
            }
        }
示例#5
0
        /// <summary>
        /// process the batch response
        /// </summary>
        /// <param name="batchReader">The batch reader to use for reading the batch response.</param>
        /// <returns>an instance of the DataServiceResponse, containing individual operation responses for this batch request.</returns>
        /// <remarks>
        /// The message reader for the entire batch response is stored in the this.batchMessageReader.
        /// The message reader is disposable, but this method should not dispose it itself. It will be either disposed by the caller (in case of exception)
        /// or the ownership will be passed to the returned response object (in case of success).
        /// In could also be diposed indirectly by this method when it enumerates through the responses.
        /// </remarks>
        private DataServiceResponse HandleBatchResponseInternal(ODataBatchReader batchReader)
        {
            Debug.Assert(this.batchMessageReader != null, "this.batchMessageReader != null");
            Debug.Assert(batchReader != null, "batchReader != null");

            DataServiceResponse response;
            HeaderCollection headers = new HeaderCollection(this.batchResponseMessage);

            IEnumerable<OperationResponse> responses = this.HandleBatchResponse(batchReader);
            if (this.Queries != null)
            {
                // ExecuteBatch, EndExecuteBatch
                response = new DataServiceResponse(
                    headers,
                    (int)this.batchResponseMessage.StatusCode,
                    responses,
                    true /*batchResponse*/);
            }
            else
            {
                List<OperationResponse> operationResponses = new List<OperationResponse>();
                response = new DataServiceResponse(headers, (int)this.batchResponseMessage.StatusCode, operationResponses, true /*batchResponse*/);
                Exception exception = null;

                // SaveChanges, EndSaveChanges
                // enumerate the entire response
                foreach (ChangeOperationResponse changeOperationResponse in responses)
                {
                    operationResponses.Add(changeOperationResponse);
                    if (Util.IsBatchWithSingleChangeset(this.Options) && exception == null && changeOperationResponse.Error != null)
                    {
                        exception = changeOperationResponse.Error;
                    }

                    // Note that this will dispose the enumerator and this release the batch message reader which is owned
                    // by the enumerable of responses by now.
                }

                // Note that if we encounter any error in a batch request with a single changeset, 
                // we throw here since all change operations in the changeset are rolled back on the server.  
                // If we encounter any error in a batch request with independent operations, we don't want to throw 
                // since some of the operations might succeed. 
                // Users need to inspect each OperationResponse to get the exception information from the failed operations.
                if (exception != null)
                {
                    throw new DataServiceRequestException(Strings.DataServiceException_GeneralError, exception, response);
                }
            }

            return response;
        }
        private static async Task<HttpRequestMessage> ReadOperationInternalAsync(
            ODataBatchReader reader, Guid batchId, Guid? changeSetId, CancellationToken cancellationToken, bool bufferContentStream = true)
        {
            ODataBatchOperationRequestMessage batchRequest = reader.CreateOperationRequestMessage();
            HttpRequestMessage request = new HttpRequestMessage();
            request.Method = new HttpMethod(batchRequest.Method);
            request.RequestUri = batchRequest.Url;

            if (bufferContentStream)
            {
                using (Stream stream = batchRequest.GetStream())
                {
                    MemoryStream bufferedStream = new MemoryStream();
                    // Passing in the default buffer size of 81920 so that we can also pass in a cancellation token
                    await stream.CopyToAsync(bufferedStream, bufferSize: 81920, cancellationToken: cancellationToken);
                    bufferedStream.Position = 0;
                    request.Content = new StreamContent(bufferedStream);
                }
            }
            else
            {
                request.Content = new LazyStreamContent(() => batchRequest.GetStream());
            }

            foreach (var header in batchRequest.Headers)
            {
                string headerName = header.Key;
                string headerValue = header.Value;
                if (!request.Headers.TryAddWithoutValidation(headerName, headerValue))
                {
                    request.Content.Headers.TryAddWithoutValidation(headerName, headerValue);
                }
            }

            request.SetODataBatchId(batchId);

            if (changeSetId != null && changeSetId.HasValue)
            {
                request.SetODataChangeSetId(changeSetId.Value);
            }

            return request;
        }
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="batchReader">The batch reader to get the batch stream from.</param>
 public BatchReaderStreamWrapper(ODataBatchReader batchReader)
 {
     this.batchStream = ReflectionUtils.GetField(batchReader, "batchStream");
     this.batchStreamBuffer = new BatchReaderStreamBufferWrapper(this.batchStream);
 }
示例#8
0
        /// <summary>
        /// Processed the operation response reported by the batch reader.
        /// This is a side-effecting method that is tied deeply to how it is used in the batch processing pipeline.
        /// </summary>
        /// <param name="batchReader">The batch reader to get the operation response from.</param>
        /// <param name="isChangesetOperation">True if the current operation is inside a changeset (implying CUD, not query)</param>
        /// <returns>An exception if the operation response is an error response, null for success response.</returns>
        private Exception ProcessCurrentOperationResponse(ODataBatchReader batchReader, bool isChangesetOperation)
        {
            Debug.Assert(batchReader != null, "batchReader != null");
            Debug.Assert(batchReader.State == ODataBatchReaderState.Operation, "This method requires the batch reader to be on an operation.");

            ODataBatchOperationResponseMessage operationResponseMessage = batchReader.CreateOperationResponseMessage();
            Descriptor descriptor = null;

            if (isChangesetOperation)
            {
                // We need to peek at the content-Id before handing the response to the user, so we can expose the Descriptor them.
                // We're OK with this exception to our general rule of not using them before ReceivingResponse event is fired.
                this.entryIndex = this.ValidateContentID(operationResponseMessage.ContentId);
                descriptor      = this.ChangedEntries[entryIndex];
            }

            // If we hit en error inside a batch, we will never expose a descriptor since we don't know which one to return.
            // The descriptor we fetched above based on the content-ID is bogus because the server returns an errounous content-id when
            // it hits an error inside batch.
            if (!WebUtil.SuccessStatusCode((HttpStatusCode)operationResponseMessage.StatusCode))
            {
                descriptor = null;
            }

            this.RequestInfo.Context.FireReceivingResponseEvent(new ReceivingResponseEventArgs(operationResponseMessage, descriptor, true));

            // We need to know if the content of the operation response is empty or not.
            // We also need to cache the entire content, since in case of GET response the response itself will be parsed
            // lazily and so it can happen that we will move the batch reader after this operation before we actually read
            // the content of the operation.
            Stream originalOperationResponseContentStream = operationResponseMessage.GetStream();

            if (originalOperationResponseContentStream == null)
            {
                Error.ThrowBatchExpectedResponse(InternalError.NullResponseStream);
            }

            MemoryStream operationResponseContentStream;

            try
            {
                operationResponseContentStream = new MemoryStream();
                WebUtil.CopyStream(originalOperationResponseContentStream, operationResponseContentStream, ref this.streamCopyBuffer);
                operationResponseContentStream.Position = 0;
            }
            finally
            {
                originalOperationResponseContentStream.Dispose();
            }

            this.currentOperationResponse = new CurrentOperationResponse(
                (HttpStatusCode)operationResponseMessage.StatusCode,
                operationResponseMessage.Headers,
                operationResponseContentStream);

            Version responseVersion;
            string  headerName = XmlConstants.HttpODataVersion;

            return(BaseSaveResult.HandleResponse(
                       this.RequestInfo,
                       this.currentOperationResponse.StatusCode,
                       this.currentOperationResponse.Headers.GetHeader(headerName),
                       () => this.currentOperationResponse.ContentStream,
                       false,
                       out responseVersion));
        }
示例#9
0
 private DataServiceResponse HandleBatchResponseInternal(ODataBatchReader batchReader)
 {
     Dictionary<string, string> headers = WebUtil.WrapResponseHeaders(base.batchResponse);
     IEnumerable<OperationResponse> enumerable = this.HandleBatchResponse(batchReader);
     if (this.Queries != null)
     {
         return new DataServiceResponse(headers, (int) base.batchResponse.StatusCode, enumerable, true);
     }
     List<OperationResponse> list = new List<OperationResponse>();
     DataServiceResponse response = new DataServiceResponse(headers, (int) base.batchResponse.StatusCode, list, true);
     Exception innerException = null;
     foreach (ChangeOperationResponse response2 in enumerable)
     {
         list.Add(response2);
         if ((innerException == null) && (response2.Error != null))
         {
             innerException = response2.Error;
         }
     }
     if (innerException != null)
     {
         throw new DataServiceRequestException(System.Data.Services.Client.Strings.DataServiceException_GeneralError, innerException, response);
     }
     return response;
 }
示例#10
0
 private IEnumerable<OperationResponse> HandleBatchResponse(ODataBatchReader batchReader)
 {
     if (this.batchMessageReader == null)
     {
         goto Label_056D;
     }
     bool iteratorVariable0 = false;
     bool iteratorVariable1 = false;
     int index = 0;
     int iteratorVariable3 = 0;
     this.entryIndex = 0;
 Label_PostSwitchInIterator:;
     while (batchReader.Read())
     {
         Exception iteratorVariable4;
         switch (batchReader.State)
         {
             case ODataBatchReaderState.Operation:
             {
                 iteratorVariable4 = this.ProcessCurrentOperationResponse(batchReader);
                 if (iteratorVariable1)
                 {
                     break;
                 }
                 QueryOperationResponse iteratorVariable5 = null;
                 try
                 {
                     if (iteratorVariable4 == null)
                     {
                         DataServiceRequest query = this.Queries[index];
                         MaterializeAtom results = DataServiceRequest.Materialize(this.RequestInfo.GetDeserializationInfo(null), query.QueryComponents(this.RequestInfo.MaxProtocolVersion), null, this.currentOperationResponse.GetHeader("Content-Type"), this.currentOperationResponse.CreateResponseMessage(), query.PayloadKind);
                         iteratorVariable5 = QueryOperationResponse.GetInstance(query.ElementType, this.currentOperationResponse.Headers, query, results);
                     }
                 }
                 catch (ArgumentException exception)
                 {
                     iteratorVariable4 = exception;
                 }
                 catch (FormatException exception2)
                 {
                     iteratorVariable4 = exception2;
                 }
                 catch (InvalidOperationException exception3)
                 {
                     iteratorVariable4 = exception3;
                 }
                 if (iteratorVariable5 == null)
                 {
                     if (this.Queries == null)
                     {
                         throw iteratorVariable4;
                     }
                     DataServiceRequest request2 = this.Queries[index];
                     if (this.RequestInfo.IgnoreResourceNotFoundException && (this.currentOperationResponse.StatusCode == HttpStatusCode.NotFound))
                     {
                         iteratorVariable5 = QueryOperationResponse.GetInstance(request2.ElementType, this.currentOperationResponse.Headers, request2, MaterializeAtom.EmptyResults);
                     }
                     else
                     {
                         iteratorVariable5 = QueryOperationResponse.GetInstance(request2.ElementType, this.currentOperationResponse.Headers, request2, MaterializeAtom.EmptyResults);
                         iteratorVariable5.Error = iteratorVariable4;
                     }
                 }
                 iteratorVariable5.StatusCode = (int) this.currentOperationResponse.StatusCode;
                 index++;
                 yield return iteratorVariable5;
                 goto Label_PostSwitchInIterator;
             }
             case ODataBatchReaderState.ChangesetStart:
             {
                 if ((this.IsBatch && iteratorVariable0) || (iteratorVariable3 != 0))
                 {
                     System.Data.Services.Client.Error.ThrowBatchUnexpectedContent(InternalError.UnexpectedBeginChangeSet);
                 }
                 iteratorVariable1 = true;
                 continue;
             }
             case ODataBatchReaderState.ChangesetEnd:
             {
                 iteratorVariable0 = true;
                 iteratorVariable3 = 0;
                 iteratorVariable1 = false;
                 continue;
             }
             default:
                 goto Label_0491;
         }
         this.entryIndex = this.ValidateContentID(this.currentOperationResponse.Headers);
         try
         {
             Descriptor descriptor = this.ChangedEntries[this.entryIndex];
             iteratorVariable3 += this.SaveResultProcessed(descriptor);
             if (iteratorVariable4 != null)
             {
                 throw iteratorVariable4;
             }
             this.HandleOperationResponseHeaders(this.currentOperationResponse.StatusCode, this.currentOperationResponse.Headers);
             this.HandleOperationResponse(descriptor, this.currentOperationResponse.Headers);
         }
         catch (Exception exception4)
         {
             this.ChangedEntries[this.entryIndex].SaveError = exception4;
             iteratorVariable4 = exception4;
             if (!CommonUtil.IsCatchableExceptionType(exception4))
             {
                 throw;
             }
         }
         ChangeOperationResponse iteratorVariable6 = new ChangeOperationResponse(this.currentOperationResponse.Headers, this.ChangedEntries[this.entryIndex]) {
             StatusCode = (int) this.currentOperationResponse.StatusCode
         };
         if (iteratorVariable4 != null)
         {
             iteratorVariable6.Error = iteratorVariable4;
         }
         iteratorVariable3++;
         this.entryIndex++;
         yield return iteratorVariable6;
         goto Label_PostSwitchInIterator;
     Label_0491:
         System.Data.Services.Client.Error.ThrowBatchExpectedResponse(InternalError.UnexpectedBatchState);
     }
     if (((this.Queries == null) && ((!iteratorVariable0 || (0 < index)) || (this.ChangedEntries.Any<Descriptor>(o => (o.ContentGeneratedForSave && (o.SaveResultWasProcessed == 0))) && (!this.IsBatch || (this.ChangedEntries.FirstOrDefault<Descriptor>(o => (o.SaveError != null)) == null))))) || ((this.Queries != null) && (index != this.Queries.Length)))
     {
         throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Batch_IncompleteResponseCount);
     }
 Label_056D:
     yield break;
 }
示例#11
0
        public static async Task <IList <HttpRequestMessage> > ReadChangeSetRequestAsync(this ODataBatchReader reader, Guid batchId, CancellationToken cancellationToken)
        {
            if (reader == null)
            {
                throw Error.ArgumentNull("reader");
            }
            if (reader.State != ODataBatchReaderState.ChangesetStart)
            {
                throw Error.InvalidOperation(
                          SRResources.InvalidBatchReaderState,
                          reader.State.ToString(),
                          ODataBatchReaderState.ChangesetStart.ToString());
            }

            Guid changeSetId = Guid.NewGuid();
            List <HttpRequestMessage> requests = new List <HttpRequestMessage>();

            while (reader.Read() && reader.State != ODataBatchReaderState.ChangesetEnd)
            {
                if (reader.State == ODataBatchReaderState.Operation)
                {
                    requests.Add(await ReadOperationInternalAsync(reader, batchId, changeSetId, cancellationToken));
                }
            }
            return(requests);
        }
示例#12
0
 public static Task <IList <HttpRequestMessage> > ReadChangeSetRequestAsync(this ODataBatchReader reader, Guid batchId)
 {
     return(reader.ReadChangeSetRequestAsync(batchId, CancellationToken.None));
 }
示例#13
0
 /// <summary>
 /// Reads an Operation request in a ChangeSet.
 /// </summary>
 /// <param name="reader">The <see cref="ODataBatchReader"/>.</param>
 /// <param name="batchId">The Batch ID.</param>
 /// <param name="changeSetId">The ChangeSet ID.</param>
 /// <param name="bufferContentStream">if set to <c>true</c> then the request content stream will be buffered.</param>
 /// <returns>A <see cref="HttpRequestMessage"/> representing a ChangeSet operation</returns>
 public static Task <HttpRequestMessage> ReadChangeSetOperationRequestAsync(this ODataBatchReader reader, Guid batchId, Guid changeSetId, bool bufferContentStream)
 {
     return(reader.ReadChangeSetOperationRequestAsync(batchId, changeSetId, bufferContentStream, CancellationToken.None));
 }
        public async Task ExecuteChangeSetAsync_ReturnsSingleErrorResponse()
        {
            // Arrange
            RequestDelegate handler = context =>
            {
                if (context.Request.Method.ToUpperInvariant() == "POST")
                {
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    return(Task.FromResult(context.Response));
                }

                return(Task.FromResult(context.Response));
            };

            UnbufferedODataBatchHandler batchHandler = new UnbufferedODataBatchHandler();
            string batchRequest = @"--86aef3eb-4af6-4750-a66d-df65e3f31ab0
Content-Type: multipart/mixed; boundary=""7a61b8c1-a80e-4e6b-bac7-2f65564e3fd6""

--7a61b8c1-a80e-4e6b-bac7-2f65564e3fd6
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: -1233709575

PUT /values HTTP/1.1
Host: example.com


--7a61b8c1-a80e-4e6b-bac7-2f65564e3fd6
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: -1854117385

POST /values HTTP/1.1
Host: example.com


--7a61b8c1-a80e-4e6b-bac7-2f65564e3fd6
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: -1665098746

DELETE /values HTTP/1.1
Host: example.com


--7a61b8c1-a80e-4e6b-bac7-2f65564e3fd6--

--86aef3eb-4af6-4750-a66d-df65e3f31ab0--";

            IEdmModel   model       = new EdmModel();
            HttpRequest request     = RequestFactory.Create("Post", "http://example.com/$batch", opt => opt.AddRouteComponents(model));
            HttpContext httpContext = request.HttpContext;

            byte[] requestBytes = Encoding.UTF8.GetBytes(batchRequest);
            httpContext.Request.Body          = new MemoryStream(requestBytes);
            httpContext.Request.ContentType   = "multipart/mixed;boundary=\"86aef3eb-4af6-4750-a66d-df65e3f31ab0\"";
            httpContext.Request.ContentLength = 431;
            httpContext.ODataFeature().RoutePrefix = "";
            IServiceProvider sp = request.GetRouteServices();

            IODataRequestMessage oDataRequestMessage = ODataMessageWrapperHelper.Create(httpContext.Request.Body, request.Headers, sp);
            ODataMessageReader   oDataMessageReader  = new ODataMessageReader(oDataRequestMessage, new ODataMessageReaderSettings {
                BaseUri = new Uri("http://example.com")
            });

            ODataBatchReader batchReader = await oDataMessageReader.CreateODataBatchReaderAsync();

            // Act
            var response = await batchHandler.ExecuteChangeSetAsync(batchReader, Guid.NewGuid(), httpContext.Request, handler);

            // Assert
            var changesetResponse = Assert.IsType <ChangeSetResponseItem>(response);
            var returnContext     = Assert.Single(changesetResponse.Contexts);

            Assert.Equal(StatusCodes.Status400BadRequest, returnContext.Response.StatusCode);
        }
        /// <summary>
        /// Executes the ChangeSet.
        /// </summary>
        /// <param name="batchReader">The batch reader.</param>
        /// <param name="batchId">The batch id.</param>
        /// <param name="originalRequest">The original request containing all the batch requests.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
        /// <returns>The response for the ChangeSet.</returns>
        public virtual async Task<ODataBatchResponseItem> ExecuteChangeSetAsync(ODataBatchReader batchReader, Guid batchId, HttpRequestMessage originalRequest, CancellationToken cancellationToken)
        {
            if (batchReader == null)
            {
                throw Error.ArgumentNull("batchReader");
            }
            if (originalRequest == null)
            {
                throw Error.ArgumentNull("originalRequest");
            }

            Guid changeSetId = Guid.NewGuid();
            List<HttpResponseMessage> changeSetResponse = new List<HttpResponseMessage>();
            Dictionary<string, string> contentIdToLocationMapping = new Dictionary<string, string>();
            try
            {
                while (batchReader.Read() && batchReader.State != ODataBatchReaderState.ChangesetEnd)
                {
                    if (batchReader.State == ODataBatchReaderState.Operation)
                    {
                        HttpRequestMessage changeSetOperationRequest = await batchReader.ReadChangeSetOperationRequestAsync(batchId, changeSetId, bufferContentStream: false);
                        changeSetOperationRequest.CopyBatchRequestProperties(originalRequest);
                        try
                        {
                            HttpResponseMessage response = await ODataBatchRequestItem.SendMessageAsync(Invoker, changeSetOperationRequest, cancellationToken, contentIdToLocationMapping);
                            if (response.IsSuccessStatusCode)
                            {
                                changeSetResponse.Add(response);
                            }
                            else
                            {
                                ChangeSetRequestItem.DisposeResponses(changeSetResponse);
                                changeSetResponse.Clear();
                                changeSetResponse.Add(response);
                                return new ChangeSetResponseItem(changeSetResponse);
                            }
                        }
                        finally
                        {
                            originalRequest.RegisterForDispose(changeSetOperationRequest.GetResourcesForDisposal());
                            originalRequest.RegisterForDispose(changeSetOperationRequest);
                        }
                    }
                }
            }
            catch
            {
                ChangeSetRequestItem.DisposeResponses(changeSetResponse);
                throw;
            }

            return new ChangeSetResponseItem(changeSetResponse);
        }
示例#16
0
 private Exception ProcessCurrentOperationResponse(ODataBatchReader batchReader)
 {
     MemoryStream stream2;
     Version version;
     IODataResponseMessage message = batchReader.CreateOperationResponseMessage();
     Stream input = message.GetStream();
     if (input == null)
     {
         System.Data.Services.Client.Error.ThrowBatchExpectedResponse(InternalError.NullResponseStream);
     }
     try
     {
         stream2 = new MemoryStream();
         WebUtil.CopyStream(input, stream2, ref this.streamCopyBuffer);
         stream2.Position = 0L;
     }
     finally
     {
         input.Dispose();
     }
     this.currentOperationResponse = new CurrentOperationResponse((HttpStatusCode) message.StatusCode, message.Headers, stream2);
     return BaseSaveResult.HandleResponse(base.RequestInfo, this.currentOperationResponse.StatusCode, this.currentOperationResponse.GetHeader("DataServiceVersion"), () => this.currentOperationResponse.ContentStream, false, out version);
 }
        private async Task<ODataResponse> ReadResponse(ODataBatchReader odataReader)
        {
            var batch = new List<ODataResponse>();

            while (odataReader.Read())
            {
                switch (odataReader.State)
                {
                    case ODataBatchReaderState.ChangesetStart:
                        break;
                    case ODataBatchReaderState.Operation:
                        var operationMessage = odataReader.CreateOperationResponseMessage();
                        if (operationMessage.StatusCode == (int)HttpStatusCode.NoContent)
                            batch.Add(ODataResponse.FromStatusCode(operationMessage.StatusCode));
                        else
                            batch.Add(await GetResponseAsync(operationMessage));
                        break;
                    case ODataBatchReaderState.ChangesetEnd:
                        break;
                }
            }

            return ODataResponse.FromBatch(batch);
        }
示例#18
0
        /// <summary>
        /// process the batch response
        /// </summary>
        /// <param name="batchReader">The batch reader to use for reading the batch response.</param>
        /// <returns>enumerable of QueryResponse or null</returns>
        /// <remarks>
        /// The batch message reader for the entire batch response is stored in this.batchMessageReader.
        /// Note that this method takes over the ownership of this reader and must Dispose it if it successfully returns.
        /// </remarks>
        private IEnumerable <OperationResponse> HandleBatchResponse(ODataBatchReader batchReader)
        {
            try
            {
                if (this.batchMessageReader == null)
                {
                    // The enumerable returned by this method can be enumerated multiple times.
                    // In that case it looks like if the method is called multiple times.
                    // This didn't fail in previous versions, it simply returned no results, so we need to do the same.
                    yield break;
                }

                Debug.Assert(batchReader != null, "batchReader != null");

                bool changesetFound  = false;
                bool insideChangeset = false;
                int  queryCount      = 0;
                int  operationCount  = 0;
                this.entryIndex = 0;
                while (batchReader.Read())
                {
                    switch (batchReader.State)
                    {
                        #region ChangesetStart
                    case ODataBatchReaderState.ChangesetStart:
                        if ((Util.IsBatchWithSingleChangeset(this.Options) && changesetFound) || (operationCount != 0))
                        {
                            // Throw if we encounter multiple changesets when running in batch with single changeset mode
                            // or if we encounter operations outside of a changeset.
                            Error.ThrowBatchUnexpectedContent(InternalError.UnexpectedBeginChangeSet);
                        }

                        insideChangeset = true;
                        break;
                        #endregion

                        #region ChangesetEnd
                    case ODataBatchReaderState.ChangesetEnd:
                        changesetFound  = true;
                        operationCount  = 0;
                        insideChangeset = false;
                        break;
                        #endregion

                        #region Operation
                    case ODataBatchReaderState.Operation:
                        Exception exception = this.ProcessCurrentOperationResponse(batchReader, insideChangeset);
                        if (!insideChangeset)
                        {
                            #region Get response
                            Debug.Assert(operationCount == 0, "missing an EndChangeSet 2");

                            QueryOperationResponse qresponse = null;
                            try
                            {
                                if (exception == null)
                                {
                                    DataServiceRequest query        = this.Queries[queryCount];
                                    ResponseInfo       responseInfo = this.RequestInfo.GetDeserializationInfo(null /*mergeOption*/);
                                    MaterializeAtom    materializer = DataServiceRequest.Materialize(
                                        responseInfo,
                                        query.QueryComponents(this.RequestInfo.Model),
                                        null,
                                        this.currentOperationResponse.Headers.GetHeader(XmlConstants.HttpContentType),
                                        this.currentOperationResponse.CreateResponseMessage(),
                                        query.PayloadKind);
                                    qresponse = QueryOperationResponse.GetInstance(query.ElementType, this.currentOperationResponse.Headers, query, materializer);
                                }
                            }
                            catch (ArgumentException e)
                            {
                                exception = e;
                            }
                            catch (FormatException e)
                            {
                                exception = e;
                            }
                            catch (InvalidOperationException e)
                            {
                                exception = e;
                            }

                            if (qresponse == null)
                            {
                                if (this.Queries != null)
                                {
                                    // this is the normal ExecuteBatch response
                                    DataServiceRequest query = this.Queries[queryCount];

                                    if (this.RequestInfo.IgnoreResourceNotFoundException && this.currentOperationResponse.StatusCode == HttpStatusCode.NotFound)
                                    {
                                        qresponse = QueryOperationResponse.GetInstance(query.ElementType, this.currentOperationResponse.Headers, query, MaterializeAtom.EmptyResults);
                                    }
                                    else
                                    {
                                        qresponse       = QueryOperationResponse.GetInstance(query.ElementType, this.currentOperationResponse.Headers, query, MaterializeAtom.EmptyResults);
                                        qresponse.Error = exception;
                                    }
                                }
                                else
                                {
                                    // This is top-level failure for SaveChanges(SaveChangesOptions.BatchWithSingleChangeset) or SaveChanges(SaveChangesOptions.BatchWithIndependentOperations) operations.
                                    // example: server doesn't support batching or number of batch objects exceeded an allowed limit.
                                    // ex could be null if the server responded to SaveChanges with an unexpected success with
                                    // response of batched GETS that did not correspond the original POST/PATCH/PUT/DELETE requests.
                                    // we expect non-null since server should have failed with a non-success code
                                    // and HandleResponse(status, ...) should generate the exception object
                                    throw exception;
                                }
                            }

                            qresponse.StatusCode = (int)this.currentOperationResponse.StatusCode;
                            queryCount++;
                            yield return(qresponse);

                            #endregion
                        }
                        else
                        {
                            #region Update response
                            try
                            {
                                Descriptor descriptor = this.ChangedEntries[this.entryIndex];
                                operationCount += this.SaveResultProcessed(descriptor);

                                if (exception != null)
                                {
                                    throw exception;
                                }

                                this.HandleOperationResponseHeaders(this.currentOperationResponse.StatusCode, this.currentOperationResponse.Headers);
#if DEBUG
                                this.HandleOperationResponse(descriptor, this.currentOperationResponse.Headers, this.currentOperationResponse.StatusCode);
#else
                                this.HandleOperationResponse(descriptor, this.currentOperationResponse.Headers);
#endif
                            }
                            catch (Exception e)
                            {
                                this.ChangedEntries[this.entryIndex].SaveError = e;
                                exception = e;

                                if (!CommonUtil.IsCatchableExceptionType(e))
                                {
                                    throw;
                                }
                            }

                            ChangeOperationResponse changeOperationResponse =
                                new ChangeOperationResponse(this.currentOperationResponse.Headers, this.ChangedEntries[this.entryIndex]);
                            changeOperationResponse.StatusCode = (int)this.currentOperationResponse.StatusCode;
                            if (exception != null)
                            {
                                changeOperationResponse.Error = exception;
                            }

                            operationCount++;
                            this.entryIndex++;
                            yield return(changeOperationResponse);

                            #endregion
                        }

                        break;
                        #endregion

                    default:
                        Error.ThrowBatchExpectedResponse(InternalError.UnexpectedBatchState);
                        break;
                    }
                }

                Debug.Assert(batchReader.State == ODataBatchReaderState.Completed, "unexpected batch state");

                // Check for a changeset without response (first line) or GET request without response (second line).
                // either all saved entries must be processed or it was a batch and one of the entries has the error
                if ((this.Queries == null &&
                     (!changesetFound ||
                      0 < queryCount ||
                      this.ChangedEntries.Any(o => o.ContentGeneratedForSave && o.SaveResultWasProcessed == 0) &&
                      (!this.IsBatchRequest || this.ChangedEntries.FirstOrDefault(o => o.SaveError != null) == null))) ||
                    (this.Queries != null && queryCount != this.Queries.Length))
                {
                    throw Error.InvalidOperation(Strings.Batch_IncompleteResponseCount);
                }
            }
            finally
            {
                // Note that this will be called only once the enumeration of all responses is finished and the Dispose
                // was called on the IEnumerator used for that enumeration. It is not called when the method returns,
                // since the compiler change this method to return the compiler-generated IEnumerable.
                Util.Dispose(ref this.batchMessageReader);
            }
        }