예제 #1
0
        /// <summary>
        /// Processes the result for successfull request and produces the actual result of the request.
        /// </summary>
        /// <typeparam name="TElement">Element type of the result.</typeparam>
        /// <param name="plan">The plan to use for the projection, if available in precompiled form.</param>
        /// <returns>A instance of QueryResponseResult created on top of of the request.</returns>
        internal QueryOperationResponse <TElement> ProcessResult <TElement>(ProjectionPlan plan)
        {
            Debug.Assert(this.responseInfo != null, "The request didn't complete yet, we don't have a response info for it.");
            MaterializeAtom materializeAtom = this.CreateMaterializer(plan, this.ServiceRequest.PayloadKind);

            return(this.GetResponse <TElement>(materializeAtom));
        }
예제 #2
0
        /// <summary>
        /// Returns the response for the request.
        /// </summary>
        /// <param name="results">materialized results for the request.</param>
        /// <typeparam name="TElement">element type of the results.</typeparam>
        /// <returns>returns the instance of QueryOperationResponse containing the response.</returns>
        internal QueryOperationResponse <TElement> GetResponse <TElement>(MaterializeAtom results)
        {
            if (this.responseMessage != null)
            {
                HeaderCollection headers = new HeaderCollection(this.responseMessage);
                QueryOperationResponse <TElement> response = new QueryOperationResponse <TElement>(headers, this.ServiceRequest, results);
                response.StatusCode = (int)this.responseMessage.StatusCode;
                return(response);
            }

            return(null);
        }
예제 #3
0
        /// <summary>
        /// Returns the response for the request.
        /// </summary>
        /// <param name="results">materialized results for the request.</param>
        /// <param name="elementType">element type of the results.</param>
        /// <returns>returns the instance of QueryOperationResponse containing the response.</returns>
        internal QueryOperationResponse GetResponseWithType(MaterializeAtom results, Type elementType)
        {
            if (this.responseMessage != null)
            {
                HeaderCollection       headers  = new HeaderCollection(this.responseMessage);
                QueryOperationResponse response = QueryOperationResponse.GetInstance(elementType, headers, this.ServiceRequest, results);
                response.StatusCode = (int)this.responseMessage.StatusCode;
                return(response);
            }

            return(null);
        }
예제 #4
0
        /// <summary>
        /// Create a parser token from xml feed
        /// </summary>
        /// <param name="reader">The xml reader</param>
        /// <remarks>The reader is expected to be placed at the beginning of the element, and after this method call, the reader will be placed
        /// at the EndElement, such that the next Element will be read in the next Read call.</remarks>
        /// <returns>token</returns>
        internal virtual PrimitiveParserToken TokenizeFromXml(XmlReader reader)
        {
            Debug.Assert(reader.NodeType == XmlNodeType.Element, "Reader at element");
            string elementString = MaterializeAtom.ReadElementString(reader, true);

            if (elementString != null)
            {
                return(new TextPrimitiveParserToken(elementString));
            }

            return(null);
        }
예제 #5
0
        /// <summary>
        /// loading a property from a response
        /// </summary>
        /// <returns>QueryOperationResponse instance containing information about the response.</returns>
        internal QueryOperationResponse LoadProperty()
        {
            MaterializeAtom results = null;

            DataServiceContext context = (DataServiceContext)this.Source;

            ClientEdmModel       model = context.Model;
            ClientTypeAnnotation type  = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(this.entity.GetType()));

            Debug.Assert(type.IsEntityType, "must be entity type to be contained");

            EntityDescriptor box = context.GetEntityDescriptor(this.entity);

            if (EntityStates.Added == box.State)
            {
                throw Error.InvalidOperation(Strings.Context_NoLoadWithInsertEnd);
            }

            ClientPropertyAnnotation property = type.GetProperty(this.propertyName, false);
            Type elementType = property.EntityCollectionItemType ?? property.NullablePropertyType;

            try
            {
                if (type.MediaDataMember == property)
                {
                    results = this.ReadPropertyFromRawData(property);
                }
                else
                {
                    results = this.ReadPropertyFromAtom(property);
                }

                return(this.GetResponseWithType(results, elementType));
            }
            catch (InvalidOperationException ex)
            {
                QueryOperationResponse response = this.GetResponseWithType(results, elementType);
                if (response != null)
                {
                    response.Error = ex;
                    throw new DataServiceQueryException(Strings.DataServiceException_GeneralError, ex, response);
                }

                throw;
            }
        }
예제 #6
0
        /// <summary>
        /// Processes the result for successfull request and produces the actual result of the request.
        /// </summary>
        /// <typeparam name="TElement">Element type of the result.</typeparam>
        /// <param name="plan">The plan to use for the projection, if available in precompiled form.</param>
        /// <returns>A instance of QueryResponseResult created on top of of the request.</returns>
        internal QueryOperationResponse <TElement> ProcessResult <TElement>(ProjectionPlan plan)
        {
            Debug.Assert(this.responseInfo != null, "The request didn't complete yet, we don't have a response info for it.");
            MaterializeAtom materializeAtom = this.CreateMaterializer(plan, this.ServiceRequest.PayloadKind);
            var             response        = this.GetResponse <TElement>(materializeAtom);

            // When query feed, the instance annotation can be materialized only when enumerating the feed.
            // So we register this action which will be called when enumerating the feed.
            materializeAtom.SetInstanceAnnotations = (instanceAnnotations) =>
            {
                if (!this.responseInfo.Context.InstanceAnnotations.ContainsKey(response))
                {
                    this.responseInfo.Context.InstanceAnnotations.Add(response, instanceAnnotations);
                }
            };
            return(response);
        }
예제 #7
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);
            }
        }
예제 #8
0
        private MaterializeAtom ReadPropertyFromRawData(ClientPropertyAnnotation property)
        {
            DataServiceContext context = (DataServiceContext)this.Source;

            bool merging = context.ApplyingChanges;

            try
            {
                context.ApplyingChanges = true;

                // if this is the data property for a media entry, what comes back
                // is the raw value (no markup)
#if ASTORIA_OPEN_OBJECT
                object openProps = null;
#endif
                string   mimeType    = null;
                Encoding encoding    = null;
                Type     elementType = property.EntityCollectionItemType ?? property.NullablePropertyType;
                IList    results     = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(elementType));
                ContentTypeUtil.ReadContentType(this.ContentType, out mimeType, out encoding);

                using (Stream responseStream = this.GetResponseStream())
                {
                    // special case byte[], and for everything else let std conversion kick-in
                    if (property.PropertyType == typeof(byte[]))
                    {
                        int    total  = checked ((int)this.ContentLength);
                        byte[] buffer = null;
                        if (total >= 0)
                        {
                            buffer = LoadPropertyResult.ReadByteArrayWithContentLength(responseStream, total);
                        }
                        else
                        {
                            buffer = LoadPropertyResult.ReadByteArrayChunked(responseStream);
                        }

                        results.Add(buffer);
#if ASTORIA_OPEN_OBJECT
                        property.SetValue(this.entity, buffer, this.propertyName, ref openProps, false);
#else
                        property.SetValue(this.entity, buffer, this.propertyName, false);
#endif
                    }
                    else
                    {
                        // responseStream will disposed, StreamReader doesn't need to dispose of it.
                        StreamReader reader         = new StreamReader(responseStream, encoding);
                        object       convertedValue = property.PropertyType == typeof(string) ?
                                                      reader.ReadToEnd() :
                                                      ClientConvert.ChangeType(reader.ReadToEnd(), property.PropertyType);
                        results.Add(convertedValue);
#if ASTORIA_OPEN_OBJECT
                        property.SetValue(this.entity, convertedValue, this.propertyName, ref openProps, false);
#else
                        property.SetValue(this.entity, convertedValue, this.propertyName, false);
#endif
                    }
                }

#if ASTORIA_OPEN_OBJECT
                Debug.Assert(openProps == null, "These should not be set in this path");
#endif
                if (property.MimeTypeProperty != null)
                {
                    // an implication of this 3rd-arg-null is that mime type properties cannot be open props
#if ASTORIA_OPEN_OBJECT
                    property.MimeTypeProperty.SetValue(this.entity, mimeType, null, ref openProps, false);
                    Debug.Assert(openProps == null, "These should not be set in this path");
#else
                    property.MimeTypeProperty.SetValue(this.entity, mimeType, null, false);
#endif
                }

                return(MaterializeAtom.CreateWrapper(context, results));
            }
            finally
            {
                context.ApplyingChanges = merging;
            }
        }
예제 #9
0
        private MaterializeAtom ReadPropertyFromAtom(ClientPropertyAnnotation property)
        {
            DataServiceContext context = (DataServiceContext)this.Source;
            bool merging = context.ApplyingChanges;

            try
            {
                context.ApplyingChanges = true;

                // store the results so that they can be there in the response body.
                Type  elementType = property.IsEntityCollection ? property.EntityCollectionItemType : property.NullablePropertyType;
                IList results     = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(elementType));

                DataServiceQueryContinuation continuation = null;

                // elementType.ElementType has Nullable stripped away, use nestedType for materializer
                using (MaterializeAtom materializer = this.GetMaterializer(this.plan))
                {
                    Debug.Assert(materializer != null, "materializer != null -- otherwise GetMaterializer() returned null rather than empty");

#if ASTORIA_OPEN_OBJECT
                    object openProperties = null;
#endif
                    // when SetLink to null, we cannot get materializer because have no-content response.
                    if (materializer.IsNoContentResponse() &&
                        property.GetValue(entity) != null &&
                        context.MergeOption != MergeOption.AppendOnly &&
                        context.MergeOption != MergeOption.NoTracking)
                    {
                        property.SetValue(this.entity, null, propertyName, false);
                    }
                    else
                    {
                        foreach (object child in materializer)
                        {
                            if (property.IsEntityCollection)
                            {
                                results.Add(child);
                            }
                            else if (property.IsPrimitiveOrEnumOrComplexCollection)
                            {
                                Debug.Assert(property.PropertyType.IsAssignableFrom(child.GetType()), "Created instance for storing collection items has to be compatible with the actual one.");

                                // Collection materialization rules requires to clear the collection if not null or set the property first and then add the collection items
                                object collectionInstance = property.GetValue(this.entity);
                                if (collectionInstance == null)
                                {
                                    // type of child has been resolved as per rules for collections so it is the correct type to instantiate
                                    collectionInstance = Activator.CreateInstance(child.GetType());

                                    // allowAdd is false - we need to assign instance as the new property value
                                    property.SetValue(this.entity, collectionInstance, this.propertyName, false /* allowAdd? */);
                                }
                                else
                                {
                                    // Clear existing collection
                                    property.ClearBackingICollectionInstance(collectionInstance);
                                }

                                foreach (var collectionItem in (IEnumerable)child)
                                {
                                    Debug.Assert(property.PrimitiveOrComplexCollectionItemType.IsAssignableFrom(collectionItem.GetType()), "Type of materialized collection items have to be compatible with the type of collection items in the actual collection property.");
                                    property.AddValueToBackingICollectionInstance(collectionInstance, collectionItem);
                                }

                                results.Add(collectionInstance);
                            }
                            else
                            {
#if ASTORIA_OPEN_OBJECT
                                property.SetValue(this.entity, child, this.propertyName, ref openProperties, false);
#else
                                // it is either primitive type, complex type or 1..1 navigation property so we just allow setting the value but not adding.
                                property.SetValue(this.entity, child, this.propertyName, false);
                                results.Add(child);
#endif
                            }
                        }
                    }

                    continuation = materializer.GetContinuation(null);
                }

                return(MaterializeAtom.CreateWrapper(context, results, continuation));
            }
            finally
            {
                context.ApplyingChanges = merging;
            }
        }
예제 #10
0
 /// <summary>
 /// constructor
 /// </summary>
 /// <param name="headers">HTTP headers</param>
 /// <param name="query">original query</param>
 /// <param name="results">retrieved objects</param>
 internal QueryOperationResponse(HeaderCollection headers, DataServiceRequest query, MaterializeAtom results)
     : base(headers, query, results)
 {
 }
예제 #11
0
 /// <summary>
 /// constructor
 /// </summary>
 /// <param name="headers">HTTP headers</param>
 /// <param name="query">original query</param>
 /// <param name="results">retrieved objects</param>
 internal QueryOperationResponse(HeaderCollection headers, DataServiceRequest query, MaterializeAtom results)
     : base(headers)
 {
     this.query   = query;
     this.results = results;
 }
예제 #12
0
        internal static QueryOperationResponse GetInstance(Type elementType, HeaderCollection headers, DataServiceRequest query, MaterializeAtom results)
        {
            Type genericType = typeof(QueryOperationResponse <>).MakeGenericType(elementType);

#if !ASTORIA_LIGHT && !PORTABLELIB
            return((QueryOperationResponse)Activator.CreateInstance(
                       genericType,
                       BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Instance,
                       null,
                       new object[] { headers, query, results },
                       System.Globalization.CultureInfo.InvariantCulture));
#else
            ConstructorInfo info = genericType.GetInstanceConstructors(false /*isPublic*/).Single();
            return((QueryOperationResponse)Util.ConstructorInvoke(info, new object[] { headers, query, results }));
#endif
        }
예제 #13
0
        internal static QueryOperationResponse GetInstance(Type elementType, HeaderCollection headers, DataServiceRequest query, MaterializeAtom results)
        {
            Type genericType = typeof(QueryOperationResponse <>).MakeGenericType(elementType);

            return((QueryOperationResponse)Activator.CreateInstance(
                       genericType,
                       BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Instance,
                       null,
                       new object[] { headers, query, results },
                       System.Globalization.CultureInfo.InvariantCulture));
        }