Exemplo n.º 1
0
        /// <summary>operation with exception</summary>
        /// <param name="e">exception object</param>
        /// <param name="response">response object</param>
        private void HandleOperationException(InvalidOperationException e, IODataResponseMessage response)
        {
            Debug.Assert(this.entryIndex >= 0 && this.entryIndex < this.ChangedEntries.Count, string.Format(System.Globalization.CultureInfo.InvariantCulture, "this.entryIndex = '{0}', this.ChangedEntries.Count = '{1}'", this.entryIndex, this.ChangedEntries.Count));

            Descriptor       current    = this.ChangedEntries[this.entryIndex];
            HeaderCollection headers    = null;
            HttpStatusCode   statusCode = HttpStatusCode.InternalServerError;

            Version responseVersion = null;

            if (response != null)
            {
                headers    = new HeaderCollection(response);
                statusCode = (HttpStatusCode)response.StatusCode;

                this.HandleOperationResponseHeaders(statusCode, headers);
                e = BaseSaveResult.HandleResponse(
                    this.RequestInfo,
                    statusCode,
                    response.GetHeader(XmlConstants.HttpODataVersion),
                    response.GetStream,
                    false /*throwOnFailure*/,
                    out responseVersion);
            }
            else
            {
                headers = new HeaderCollection();
                headers.SetHeader(XmlConstants.HttpContentType, XmlConstants.MimeTextPlain);

                // In V2 we used to merge individual responses from a call to SaveChanges() into a single batch response payload and then process that.
                // When we encounter an exception at this point in V2, we used to write the exception to the batch response payload and later on when we
                // process through the batch response, we create a DataServiceClientException for each failed operation.
                // For backcompat reason, we will always convert the exception type to DataServiceClientException here.
                Debug.Assert(e != null, "e != null");
                if (e.GetType() != typeof(DataServiceClientException))
                {
                    e = new DataServiceClientException(e.Message, e);
                }
            }

            // For error scenarios, we never invoke the ReadingEntity event.
            this.cachedResponses.Add(new CachedResponse(current, headers, statusCode, responseVersion, null, e));
            this.perRequest = null;
            this.CheckContinueOnError();
        }
Exemplo n.º 2
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));
        }
Exemplo n.º 3
0
        /// <summary>
        /// process the batch response
        /// </summary>
        /// <returns>an instance of the DataServiceResponse, containing individual operation responses for this batch request.</returns>
        private DataServiceResponse HandleBatchResponse()
        {
            bool batchMessageReaderOwned = true;

            try
            {
                if ((this.batchResponseMessage == null) || (this.batchResponseMessage.StatusCode == (int)HttpStatusCode.NoContent))
                {   // we always expect a response to our batch POST request
                    throw Error.InvalidOperation(Strings.Batch_ExpectedResponse(1));
                }

                Func <Stream> getResponseStream = () => this.ResponseStream;

                // We are not going to use the responseVersion returned from this call, as the $batch request itself doesn't apply versioning
                // of the responses on the root level. The responses are versioned on the part level. (Note that the version on the $batch level
                // is actually used to version the batch itself, but we for now we only recognize a single version so to keep it backward compatible
                // we don't check this here. Also note that the HandleResponse method will verify that we can support the version, that is it's
                // lower than the highest version we understand).
                Version responseVersion;
                BaseSaveResult.HandleResponse(
                    this.RequestInfo,
                    (HttpStatusCode)this.batchResponseMessage.StatusCode,               // statusCode
                    this.batchResponseMessage.GetHeader(XmlConstants.HttpODataVersion), // responseVersion
                    getResponseStream,                                                  // getResponseStream
                    true,                                                               // throwOnFailure
                    out responseVersion);

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

                // Create the message and the message reader.
                this.batchResponseMessage = new HttpWebResponseMessage(new HeaderCollection(this.batchResponseMessage), this.batchResponseMessage.StatusCode, getResponseStream);
                ODataMessageReaderSettings messageReaderSettings = this.RequestInfo.GetDeserializationInfo(/*mergeOption*/ null).ReadHelper.CreateSettings();

                // No need to pass in any model to the batch reader.
                this.batchMessageReader = new ODataMessageReader(this.batchResponseMessage, messageReaderSettings);
                ODataBatchReader batchReader;
                try
                {
                    batchReader = this.batchMessageReader.CreateODataBatchReader();
                }
                catch (ODataContentTypeException contentTypeException)
                {
                    string    mime;
                    Encoding  encoding;
                    Exception inner = contentTypeException;
                    ContentTypeUtil.ReadContentType(this.batchResponseMessage.GetHeader(XmlConstants.HttpContentType), out mime, out encoding);
                    if (String.Equals(XmlConstants.MimeTextPlain, mime))
                    {
                        inner = GetResponseText(
                            this.batchResponseMessage.GetStream,
                            (HttpStatusCode)this.batchResponseMessage.StatusCode);
                    }

                    throw Error.InvalidOperation(Strings.Batch_ExpectedContentType(this.batchResponseMessage.GetHeader(XmlConstants.HttpContentType)), inner);
                }

                DataServiceResponse response = this.HandleBatchResponseInternal(batchReader);

                // In case of successful processing of at least the beginning of the batch, the message reader is owned by the returned response
                // (or rather by the IEnumerable of operation responses inside it).
                // It will be disposed once the operation responses are enumerated (since the IEnumerator should be disposed once used).
                // In that case we must NOT dispose it here, since that enumeration can exist long after we return from this method.
                batchMessageReaderOwned = false;

                return(response);
            }
            catch (DataServiceRequestException)
            {
                throw;
            }
            catch (InvalidOperationException ex)
            {
                HeaderCollection headers     = new HeaderCollection(this.batchResponseMessage);
                int statusCode               = this.batchResponseMessage == null ? (int)HttpStatusCode.InternalServerError : (int)this.batchResponseMessage.StatusCode;
                DataServiceResponse response = new DataServiceResponse(headers, statusCode, new OperationResponse[0], this.IsBatchRequest);
                throw new DataServiceRequestException(Strings.DataServiceException_GeneralError, ex, response);
            }
            finally
            {
                if (batchMessageReaderOwned)
                {
                    Util.Dispose(ref this.batchMessageReader);
                }
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Handle the response payload.
        /// </summary>
        /// <param name="responseMsg">httpwebresponse instance.</param>
        /// <param name="responseStream">stream containing the response payload.</param>
        private void HandleOperationResponseData(IODataResponseMessage responseMsg, Stream responseStream)
        {
            Debug.Assert(this.entryIndex >= 0 && this.entryIndex < this.ChangedEntries.Count, string.Format(System.Globalization.CultureInfo.InvariantCulture, "this.entryIndex = '{0}', this.ChangedEntries.Count() = '{1}'", this.entryIndex, this.ChangedEntries.Count));

            // Parse the response
            Descriptor        current = this.ChangedEntries[this.entryIndex];
            MaterializerEntry entry   = default(MaterializerEntry);
            Version           responseVersion;
            Exception         exception = BaseSaveResult.HandleResponse(this.RequestInfo, (HttpStatusCode)responseMsg.StatusCode, responseMsg.GetHeader(XmlConstants.HttpODataVersion), () => { return(responseStream); }, false /*throwOnFailure*/, out responseVersion);

            var headers = new HeaderCollection(responseMsg);

            if (responseStream != null && current.DescriptorKind == DescriptorKind.Entity && exception == null)
            {
                // Only process the response if the current resource is an entity and it's an insert or update scenario
                EntityDescriptor entityDescriptor = (EntityDescriptor)current;

                // We were ignoring the payload for non-insert and non-update scenarios. We need to keep doing that.
                if (entityDescriptor.State == EntityStates.Added || entityDescriptor.StreamState == EntityStates.Added ||
                    entityDescriptor.State == EntityStates.Modified || entityDescriptor.StreamState == EntityStates.Modified)
                {
                    try
                    {
                        ResponseInfo responseInfo           = this.CreateResponseInfo(entityDescriptor);
                        var          responseMessageWrapper = new HttpWebResponseMessage(
                            headers,
                            responseMsg.StatusCode,
                            () => responseStream);

                        entry = ODataReaderEntityMaterializer.ParseSingleEntityPayload(responseMessageWrapper, responseInfo, entityDescriptor.Entity.GetType());
                        entityDescriptor.TransientEntityDescriptor = entry.EntityDescriptor;
                    }
                    catch (Exception ex)
                    {
                        exception = ex;

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

            this.cachedResponses.Add(new CachedResponse(
                                         current,
                                         headers,
                                         (HttpStatusCode)responseMsg.StatusCode,
                                         responseVersion,
                                         entry,
                                         exception));

            if (exception != null)
            {
                current.SaveError = exception;

                // DEVNOTE(pqian):
                // There are two possible scenario here:
                // 1. We are in the sync code path, and there's an in stream error on the server side, or there are bad xml thrown
                // 2. We are in the async code path, there's a error thrown on the server side (any error)
                // Ideally, we need to check whether we want to continue to the next changeset. (Call this.CheckContinueOnError)
                // However, in V1/V2, we did not do this. Thus we will always continue on error on these scenarios
            }
        }