예제 #1
0
 /// <summary>constructor</summary>
 /// <param name="entity">entity</param>
 /// <param name="propertyName">name of collection or reference property to load</param>
 /// <param name="context">Originating context</param>
 /// <param name="request">Originating WebRequest</param>
 /// <param name="callback">user callback</param>
 /// <param name="state">user state</param>
 /// <param name="dataServiceRequest">request object.</param>
 /// <param name="plan">Projection plan for materialization; possibly null.</param>
 /// <param name="isContinuation">Whether this request is a continuation request.</param>
 internal LoadPropertyResult(object entity, string propertyName, DataServiceContext context, ODataRequestMessageWrapper request, AsyncCallback callback, object state, DataServiceRequest dataServiceRequest, ProjectionPlan plan, bool isContinuation)
     : base(context, Util.LoadPropertyMethodName, dataServiceRequest, request, new RequestInfo(context, isContinuation), callback, state)
 {
     this.entity = entity;
     this.propertyName = propertyName;
     this.plan = plan;
 }
예제 #2
0
 /// <summary>
 /// Constructs a new async result object
 /// </summary>
 /// <param name="context">The source of the operation.</param>
 /// <param name="method">Name of the method which is invoked asynchronously.</param>
 /// <param name="request">The <see cref="HttpWebRequest"/> object which is wrapped by this async result.</param>
 /// <param name="callback">User specified callback for the async operation.</param>
 /// <param name="state">User state for the async callback.</param>
 /// <param name="streamDescriptor">stream descriptor whose value is getting queried.</param>
 internal GetReadStreamResult(
     DataServiceContext context,
     string method,
     ODataRequestMessageWrapper request,
     AsyncCallback callback,
     object state,
     StreamDescriptor streamDescriptor)
     : base(context, method, callback, state)
 {
     Debug.Assert(request != null, "Null request can't be wrapped to a result.");
     Debug.Assert(streamDescriptor != null, "streamDescriptor != null");
     this.requestMessage = request;
     this.Abortable = request;
     this.streamDescriptor = streamDescriptor;
     this.requestInfo = new RequestInfo(context);
 }
예제 #3
0
        /// <summary>
        /// Create request message from the next change.
        /// </summary>
        /// <returns>An instance of ODataRequestMessage for the next change.</returns>
        private ODataRequestMessageWrapper CreateNextRequest()
        {
            bool moveForward = this.streamRequestKind == StreamRequestKind.None;

            if (unchecked ((uint)this.entryIndex < (uint)this.ChangedEntries.Count))
            {
                Descriptor previousDescriptor = this.ChangedEntries[this.entryIndex];
                if (previousDescriptor.DescriptorKind == DescriptorKind.Entity)
                {
                    EntityDescriptor entityDescriptor = (EntityDescriptor)previousDescriptor;

                    // In any case also close the save stream if there's any and forget about it
                    // for POST this is just a good practice to do so as soon as possible
                    // for PUT it's actually required for us to recognize that we already processed the MR part of the change
                    entityDescriptor.CloseSaveStream();

                    // If the previous request was an MR request the next one might be a PUT for the MLE
                    //   but if the entity was not changed (just the MR changed) no PUT for MLE should be sent
                    if (this.streamRequestKind == StreamRequestKind.PutMediaResource && EntityStates.Unchanged == entityDescriptor.State)
                    {
                        // Only the MR changed. In this case we also need to mark the descriptor as processed to notify
                        //   that the content for save has been generated as there's not going to be another request for it.
                        entityDescriptor.ContentGeneratedForSave = true;
                        moveForward = true;
                    }
                }
                else if (previousDescriptor.DescriptorKind == DescriptorKind.NamedStream)
                {
                    ((StreamDescriptor)previousDescriptor).CloseSaveStream();
                }
            }

            if (moveForward)
            {
                this.entryIndex++;
            }

            ODataRequestMessageWrapper requestMessage = null;

            if (unchecked ((uint)this.entryIndex < (uint)this.ChangedEntries.Count))
            {
                Descriptor descriptor = this.ChangedEntries[this.entryIndex];
                Descriptor descriptorForSendingRequest2 = descriptor;
                if (descriptor.DescriptorKind == DescriptorKind.Entity)
                {
                    EntityDescriptor entityDescriptor = (EntityDescriptor)descriptor;

                    if (((descriptor.State == EntityStates.Unchanged) || (descriptor.State == EntityStates.Modified)) &&
                        ((requestMessage = this.CheckAndProcessMediaEntryPut(entityDescriptor)) != null))
                    {
                        this.streamRequestKind       = StreamRequestKind.PutMediaResource;
                        descriptorForSendingRequest2 = entityDescriptor.DefaultStreamDescriptor;  // We want to give SendingRequest2 the StreamDescriptor
                    }
                    else if ((descriptor.State == EntityStates.Added) && ((requestMessage = this.CheckAndProcessMediaEntryPost(entityDescriptor)) != null))
                    {
                        this.streamRequestKind = StreamRequestKind.PostMediaResource;

                        Debug.Assert(entityDescriptor.SaveStream == null || entityDescriptor.StreamState == EntityStates.Added, "Either this is a V1 MR or the stream must be in added state");

                        // Set the stream state to added
                        // For V1 scenarios, when SetSaveStream was not called, this might not be in Added state
                        entityDescriptor.StreamState = EntityStates.Added;
                    }
                    else
                    {
                        this.streamRequestKind = StreamRequestKind.None;
                        Debug.Assert(descriptor.State != EntityStates.Unchanged, "descriptor.State != EntityStates.Unchanged");
                        requestMessage = this.CreateRequest(entityDescriptor);
                    }
                }
                else if (descriptor.DescriptorKind == DescriptorKind.NamedStream)
                {
                    requestMessage = this.CreateNamedStreamRequest((StreamDescriptor)descriptor);
                }
                else
                {
                    requestMessage = this.CreateRequest((LinkDescriptor)descriptor);
                }

                if (requestMessage != null)
                {
                    // we need to fire request after the headers have been written, but before we write the payload
                    // also in the prototype we are firing SendingRequest2 before SendingRequest.
                    requestMessage.FireSendingEventHandlers(descriptorForSendingRequest2);
                }
            }

            return(requestMessage);
        }
예제 #4
0
        /// <summary>
        /// Writes the body operation parameters associated with a ServiceAction. For each BodyOperationParameter:
        /// 1. calls ODataPropertyConverter  to convert CLR object into ODataValue/primitive values.
        /// 2. then calls ODataParameterWriter to write the ODataValue/primitive values.
        /// </summary>
        /// <param name="operationParameters">The list of operation parameters to write.</param>
        /// <param name="requestMessage">The OData request message used to write the operation parameters.</param>
        internal void WriteBodyOperationParameters(List<BodyOperationParameter> operationParameters, ODataRequestMessageWrapper requestMessage)
        {
            Debug.Assert(requestMessage != null, "requestMessage != null");
            Debug.Assert(operationParameters != null, "operationParameters != null");
            Debug.Assert(operationParameters.Any(), "operationParameters.Any()");

            using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, true /*isParameterPayload*/))
            {
                ODataParameterWriter parameterWriter = messageWriter.CreateODataParameterWriter(null);
                parameterWriter.WriteStart();

                foreach (BodyOperationParameter operationParameter in operationParameters)
                {
                    if (operationParameter.Value == null)
                    {
                        parameterWriter.WriteValue(operationParameter.Name, operationParameter.Value);
                    }
                    else
                    {
                        ClientEdmModel model = this.requestInfo.Model;
                        IEdmType edmType = model.GetOrCreateEdmType(operationParameter.Value.GetType());
                        Debug.Assert(edmType != null, "edmType != null");

                        switch (edmType.TypeKind)
                        {
                            case EdmTypeKind.Collection:
                                {
                                    this.WriteCollectionValueInBodyOperationParameter(parameterWriter, operationParameter, (IEdmCollectionType)edmType);
                                    break;
                                }

                            case EdmTypeKind.Entity:
                                {
                                    Debug.Assert(model.GetClientTypeAnnotation(edmType).ElementType != null, "model.GetClientTypeAnnotation(edmType).ElementType != null");
                                    ODataEntry entry = this.CreateODataEntryFromEntityOperationParameter(model.GetClientTypeAnnotation(edmType), operationParameter.Value);
                                    Debug.Assert(entry != null, "entry != null");
                                    var entryWriter = parameterWriter.CreateEntryWriter(operationParameter.Name);
                                    entryWriter.WriteStart(entry);
                                    entryWriter.WriteEnd();
                                    break;
                                }

                            case EdmTypeKind.Complex:
                                {
                                    Debug.Assert(model.GetClientTypeAnnotation(edmType).ElementType != null, "model.GetClientTypeAnnotation(edmType).ElementType != null");
                                    ODataComplexValue complexValue = this.propertyConverter.CreateODataComplexValue(
                                        model.GetClientTypeAnnotation(edmType).ElementType,
                                        operationParameter.Value,
                                        null /*propertyName*/,
                                        false /*isCollectionItemType*/,
                                        null /*visitedComplexTypeObjects*/);

                                    Debug.Assert(complexValue != null, "complexValue != null");
                                    parameterWriter.WriteValue(operationParameter.Name, complexValue);
                                    break;
                                }

                            case EdmTypeKind.Primitive:
                                object primitiveValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(operationParameter.Value, operationParameter.Value.GetType());
                                parameterWriter.WriteValue(operationParameter.Name, primitiveValue);
                                break;

                            case EdmTypeKind.Enum:
                                ODataEnumValue tmp = this.propertyConverter.CreateODataEnumValue(
                                    model.GetClientTypeAnnotation(edmType).ElementType,
                                    operationParameter.Value,
                                    false);
                                parameterWriter.WriteValue(operationParameter.Name, tmp);

                                break;
                            default:
                                // EdmTypeKind.Row
                                // EdmTypeKind.EntityReference
                                throw new NotSupportedException(Strings.Serializer_InvalidParameterType(operationParameter.Name, edmType.TypeKind));
                        }
                    } // else
                } // foreach

                parameterWriter.WriteEnd();
                parameterWriter.Flush();
            }
        }
예제 #5
0
 /// <summary>
 /// Writes an entity reference link.
 /// </summary>
 /// <param name="binding">The link descriptor.</param>
 /// <param name="requestMessage">The request message used for writing the payload.</param>
 /// <param name="isBatch">True if batch, false otherwise.</param>
 internal void WriteEntityReferenceLink(LinkDescriptor binding, ODataRequestMessageWrapper requestMessage, bool isBatch)
예제 #6
0
        /// <summary>
        /// Returns the request message to write the headers and payload into.
        /// </summary>
        /// <param name="method">Http method for the request.</param>
        /// <param name="requestUri">Base Uri for the request.</param>
        /// <param name="headers">Request headers.</param>
        /// <param name="httpStack">HttpStack to use.</param>
        /// <param name="descriptor">Descriptor for the request, if there is one.</param>
        /// <param name="contentId">Content-ID header that could be used in batch request.</param>
        /// <returns>an instance of IODataRequestMessage.</returns>
        protected override ODataRequestMessageWrapper CreateRequestMessage(string method, Uri requestUri, HeaderCollection headers, HttpStack httpStack, Descriptor descriptor, string contentId)
        {
            BuildingRequestEventArgs args = this.RequestInfo.CreateRequestArgsAndFireBuildingRequest(method, requestUri, headers, this.RequestInfo.HttpStack, descriptor);

            return(ODataRequestMessageWrapper.CreateBatchPartRequestMessage(this.batchWriter, args, this.RequestInfo, contentId));
        }
예제 #7
0
        /// <summary>
        /// Synchronizely get the query set count from the server by executing the $count=value query
        /// </summary>
        /// <param name="context">The context</param>
        /// <returns>The server side count of the query set</returns>
        internal long GetQuerySetCount(DataServiceContext context)
        {
            Debug.Assert(null != context, "context is null");
            Version requestVersion = this.QueryComponents(context.Model).Version;

            if (requestVersion == null)
            {
                requestVersion = Util.ODataVersion4;
            }

            QueryResult               response       = null;
            QueryComponents           qc             = this.QueryComponents(context.Model);
            Uri                       requestUri     = qc.Uri;
            DataServiceRequest <long> serviceRequest = new DataServiceRequest <long>(requestUri, qc, null);

            HeaderCollection headers = new HeaderCollection();

            // Validate and set the request DSV header
            headers.SetRequestVersion(requestVersion, context.MaxProtocolVersionAsVersion);
            context.Format.SetRequestAcceptHeaderForCount(headers);

            string httpMethod = XmlConstants.HttpMethodGet;
            ODataRequestMessageWrapper request = context.CreateODataRequestMessage(
                context.CreateRequestArgsAndFireBuildingRequest(httpMethod, requestUri, headers, context.HttpStack, null /*descriptor*/),
                null /*descriptor*/);

            response = new QueryResult(this, Util.ExecuteMethodName, serviceRequest, request, new RequestInfo(context), null, null);

            try
            {
                response.ExecuteQuery();

                if (HttpStatusCode.NoContent != response.StatusCode)
                {
                    StreamReader sr = new StreamReader(response.GetResponseStream());
                    long         r  = -1;
                    try
                    {
                        r = XmlConvert.ToInt64(sr.ReadToEnd());
                    }
                    finally
                    {
                        sr.Close();
                    }

                    return(r);
                }
                else
                {
                    throw new DataServiceQueryException(Strings.DataServiceRequest_FailGetCount, response.Failure);
                }
            }
            catch (InvalidOperationException ex)
            {
                QueryOperationResponse operationResponse = null;
                operationResponse = response.GetResponse <long>(MaterializeAtom.EmptyResults);
                if (null != operationResponse)
                {
                    operationResponse.Error = ex;
                    throw new DataServiceQueryException(Strings.DataServiceException_GeneralError, ex, operationResponse);
                }

                throw;
            }
        }
예제 #8
0
        protected override void AsyncEndGetResponse(IAsyncResult asyncResult)
        {
            Debug.Assert(asyncResult != null && asyncResult.IsCompleted, "asyncResult.IsCompleted");
            AsyncStateBag asyncStateBag = asyncResult.AsyncState as AsyncStateBag;

            PerRequest pereq = asyncStateBag == null ? null : asyncStateBag.PerRequest;

            try
            {
                if (this.IsAborted)
                {
                    if (pereq != null)
                    {
                        pereq.SetComplete();
                    }

                    this.SetCompleted();
                }
                else
                {
                    this.CompleteCheck(pereq, InternalError.InvalidEndGetResponseCompleted);
                    pereq.SetRequestCompletedSynchronously(asyncResult.CompletedSynchronously);
                    this.SetCompletedSynchronously(asyncResult.CompletedSynchronously);

                    ODataRequestMessageWrapper requestMessage = Util.NullCheck(pereq.Request, InternalError.InvalidEndGetResponseRequest);

                    // the httpWebResponse is kept for batching, discarded by non-batch
                    IODataResponseMessage response = this.RequestInfo.EndGetResponse(requestMessage, asyncResult);
                    pereq.ResponseMessage = Util.NullCheck(response, InternalError.InvalidEndGetResponseResponse);

                    this.SetHttpWebResponse(pereq.ResponseMessage);

                    Debug.Assert(null == pereq.ResponseStream, "non-null async ResponseStream");
                    Stream httpResponseStream = null;

                    if (HttpStatusCode.NoContent != (HttpStatusCode)response.StatusCode)
                    {
                        httpResponseStream   = response.GetStream();
                        pereq.ResponseStream = httpResponseStream;
                    }

                    if ((null != httpResponseStream) && httpResponseStream.CanRead)
                    {
                        if (null == this.outputResponseStream)
                        {
                            // this is the stream we copy the reponse to
                            this.outputResponseStream = Util.NullCheck(this.GetAsyncResponseStreamCopy(), InternalError.InvalidAsyncResponseStreamCopy);
                        }

                        if (null == this.asyncStreamCopyBuffer)
                        {
                            // this is the buffer we read into and copy out of
                            this.asyncStreamCopyBuffer = Util.NullCheck(this.GetAsyncResponseStreamCopyBuffer(), InternalError.InvalidAsyncResponseStreamCopyBuffer);
                        }

                        // Make async calls to read the response stream
                        this.ReadResponseStream(asyncStateBag);
                    }
                    else
                    {
                        pereq.SetComplete();
                        this.SetCompleted();
                    }
                }
            }
            catch (Exception e)
            {
                if (this.HandleFailure(e))
                {
                    throw;
                }
            }
            finally
            {
                this.HandleCompleted(pereq);
            }
        }
 /// <summary>
 /// This method wraps the HttpWebRequest.GetSyncronousResponse method call. The reasons for doing this are to give us a place
 /// to invoke internal test hook callbacks that can validate the response headers, and also so that we can do
 /// debug validation to make sure that the headers have not changed since they were originally configured on the request.
 /// </summary>
 /// <param name="request">ODataRequestMessageWrapper instance</param>
 /// <param name="handleWebException">If set to true, this method will only re-throw the WebException that was caught if
 /// the response in the exception is null. If set to false, this method will always re-throw in case of a WebException.</param>
 /// <returns>
 /// Returns the HttpWebResponse from the wrapped GetSyncronousResponse method.
 /// </returns>
 internal IODataResponseMessage GetSyncronousResponse(ODataRequestMessageWrapper request, bool handleWebException)
 {
     return(this.Context.GetSyncronousResponse(request, handleWebException));
 }
예제 #10
0
        /// <summary>
        /// Create request message for the descriptor at the given index.
        /// </summary>
        /// <param name="index">Index into changed entries</param>
        /// <param name="requestMessage">IODataRequestMessage that needs to be used for writing the payload.</param>
        /// <returns>true, if any request payload was generated, else false.</returns>
        protected bool CreateChangeData(int index, ODataRequestMessageWrapper requestMessage)
        {
            Descriptor descriptor = this.ChangedEntries[index];
            Debug.Assert(!descriptor.ContentGeneratedForSave, "already saved entity/link");

            // Since batch payloads do not support MR and Named Stream, the code for handling that has been moved to
            // SaveResult.CreateNonBatchChangeData. In this method, we only handle entity and link payload.
            Debug.Assert(descriptor.DescriptorKind != DescriptorKind.NamedStream, "NamedStream payload is not supported in batch");

            if (descriptor.DescriptorKind == DescriptorKind.Entity)
            {
                EntityDescriptor entityDescriptor = (EntityDescriptor)descriptor;
                Debug.Assert(this.streamRequestKind == StreamRequestKind.None, "Batch does not support stream payloads. Hence this code should not get called at all");

                // either normal entity or second call for media link entity, generate content payload
                // else first call of media link descriptor where we only send the default value
                descriptor.ContentGeneratedForSave = true;
                return this.CreateRequestData(entityDescriptor, requestMessage);
            }
            else
            {
                descriptor.ContentGeneratedForSave = true;
                LinkDescriptor link = (LinkDescriptor)descriptor;
                if ((EntityStates.Added == link.State) ||
                    ((EntityStates.Modified == link.State) && (null != link.Target)))
                {
                    this.CreateRequestData(link, requestMessage);
                    return true;
                }
            }

            return false;
        }
예제 #11
0
 /// <summary>
 /// This method wraps the HttpWebRequest.GetSyncronousResponse method call. The reasons for doing this are to give us a place
 /// to invoke internal test hook callbacks that can validate the response headers, and also so that we can do
 /// debug validation to make sure that the headers have not changed since they were originally configured on the request.
 /// </summary>
 /// <param name="request">ODataRequestMessageWrapper instance</param>
 /// <param name="handleWebException">If set to true, this method will only re-throw the WebException that was caught if
 /// the response in the exception is null. If set to false, this method will always re-throw in case of a WebException.</param>
 /// <returns>
 /// Returns the HttpWebResponse from the wrapped GetSyncronousResponse method.
 /// </returns>
 internal IODataResponseMessage GetSyncronousResponse(ODataRequestMessageWrapper request, bool handleWebException)
 {
     return this.Context.GetSyncronousResponse(request, handleWebException);
 }
예제 #12
0
        /// <summary>
        /// Generate a request for the given entity.
        /// </summary>
        /// <param name="entityDescriptor">Instance of EntityDescriptor.</param>
        /// <param name="requestMessage">Instance of IODataRequestMessage to be used to generate the payload.</param>
        /// <returns>True if the payload was generated, otherwise false.</returns>
        private bool CreateRequestData(EntityDescriptor entityDescriptor, ODataRequestMessageWrapper requestMessage)
        {
            Debug.Assert(null != entityDescriptor, "null entityDescriptor");
            bool generateRequestPayload = false;
            switch (entityDescriptor.State)
            {
                case EntityStates.Deleted:
                    break;
                case EntityStates.Modified:
                case EntityStates.Added:
                    generateRequestPayload = true;
                    break;
                default:
                    Error.ThrowInternalError(InternalError.UnvalidatedEntityState);
                    break;
            }

            if (generateRequestPayload)
            {
                Debug.Assert(this.SerializerInstance != null, "this.SerializerInstance != null");
                this.SerializerInstance.WriteEntry(entityDescriptor, this.RelatedLinks(entityDescriptor), requestMessage);
            }

            return generateRequestPayload;
        }
예제 #13
0
        /// <summary>
        /// Generate a request for the given link.
        /// </summary>
        /// <param name="binding">Instance of LinkDescriptor.</param>
        /// <param name="requestMessage">Instance of IODataRequestMessage to be used to generate the payload.</param>
        private void CreateRequestData(LinkDescriptor binding, ODataRequestMessageWrapper requestMessage)
        {
            Debug.Assert(
                (binding.State == EntityStates.Added) ||
                (binding.State == EntityStates.Modified && null != binding.Target),
                "This method must be called only when a binding is added or put");
#if DEBUG
            Debug.Assert(!Util.IsBatchWithSingleChangeset(this.Options) || this.IsBatchRequest, "If this.Options.IsBatchWithSingleChangeset() is true, this.IsBatchRequest must also be true.");
            this.SerializerInstance.WriteEntityReferenceLink(binding, requestMessage, Util.IsBatchWithSingleChangeset(this.Options));
#else
            this.SerializerInstance.WriteEntityReferenceLink(binding, requestMessage);
#endif
        }
예제 #14
0
        /// <summary>
        /// Create memory stream for descriptor (entity or link or MR or named stream).
        /// </summary>
        /// <param name="index">Index into changed entries.</param>
        /// <param name="requestMessage">RequestMessage to be used to generate the payload.</param>
        /// <returns>Stream of data for descriptor.</returns>
        protected ContentStream CreateNonBatchChangeData(int index, ODataRequestMessageWrapper requestMessage)
        {
            Descriptor descriptor = this.ChangedEntries[index];
            Debug.Assert(!descriptor.ContentGeneratedForSave, "already saved entity/link");
            Debug.Assert(!this.IsBatchRequest, "we do not support MR requests in batch");

            if (descriptor.DescriptorKind == DescriptorKind.Entity && this.streamRequestKind != StreamRequestKind.None)
            {
                if (this.streamRequestKind != StreamRequestKind.None)
                {
                    Debug.Assert(
                        this.streamRequestKind == StreamRequestKind.PutMediaResource || descriptor.State == EntityStates.Modified,
                        "We should have modified the MLE state to Modified when we've created the MR POST request.");
                    Debug.Assert(
                        this.streamRequestKind != StreamRequestKind.PutMediaResource || (descriptor.State == EntityStates.Unchanged || descriptor.State == EntityStates.Modified),
                        "If we're processing MR PUT the entity must be either in Unchanged or Modified state.");

                    // media resource request - we already precreated the body of the request
                    // in the CheckAndProcessMediaEntryPost or CheckAndProcessMediaEntryPut method.
                    Debug.Assert(this.mediaResourceRequestStream != null, "We should have precreated the MR stream already.");
                    return new ContentStream(this.mediaResourceRequestStream, false);
                }
            }
            else if (descriptor.DescriptorKind == DescriptorKind.NamedStream)
            {
                Debug.Assert(!this.IsBatchRequest, "we do not support named stream requests in batch");

                // named stream request - we already precreated the body of the request in CreateNamedStreamRequest method
                Debug.Assert(this.mediaResourceRequestStream != null, "We should have precreated the MR stream already.");
                descriptor.ContentGeneratedForSave = true;
                return new ContentStream(this.mediaResourceRequestStream, false);
            }
            else if (this.CreateChangeData(index, requestMessage))
            {
                return requestMessage.CachedRequestStream;
            }

            return null;
        }
예제 #15
0
        internal void BeginCreateNextChange()
        {
            Debug.Assert(!this.IsCompletedInternally, "why being called if already completed?");

            // create the memory stream required to cache the responses as we read async from the underlying http web response
            this.inMemoryResponseStream = new MemoryStream();

            // SaveCallback can't chain synchronously completed responses, caller will loop the to next change
            PerRequest   pereq       = null;
            IAsyncResult asyncResult = null;

            do
            {
                IODataResponseMessage      responseMsg    = null;
                ODataRequestMessageWrapper requestMessage = null;
                try
                {
                    if (this.perRequest != null)
                    {
                        this.SetCompleted();
                        Error.ThrowInternalError(InternalError.InvalidBeginNextChange);
                    }

                    requestMessage = this.CreateNextRequest();

                    // Keeping the old behavior (V1/V2) where the abortable was set to null,
                    // if CreateNextRequest returned null.
                    if (requestMessage == null)
                    {
                        this.Abortable = null;
                    }

                    if ((requestMessage != null) || (this.entryIndex < this.ChangedEntries.Count))
                    {
                        if (this.ChangedEntries[this.entryIndex].ContentGeneratedForSave)
                        {
                            Debug.Assert(this.ChangedEntries[this.entryIndex] is LinkDescriptor, "only expected RelatedEnd to presave");
                            Debug.Assert(
                                this.ChangedEntries[this.entryIndex].State == EntityStates.Added ||
                                this.ChangedEntries[this.entryIndex].State == EntityStates.Modified,
                                "only expected added to presave");
                            continue;
                        }

                        this.Abortable = requestMessage;
                        ContentStream contentStream = this.CreateNonBatchChangeData(this.entryIndex, requestMessage);
                        this.perRequest = pereq = new PerRequest();
                        pereq.Request   = requestMessage;

                        AsyncStateBag asyncStateBag = new AsyncStateBag(pereq);

                        if (contentStream == null || contentStream.Stream == null)
                        {
                            asyncResult = BaseAsyncResult.InvokeAsync(requestMessage.BeginGetResponse, this.AsyncEndGetResponse, asyncStateBag);
                        }
                        else
                        {
                            if (contentStream.IsKnownMemoryStream)
                            {
                                requestMessage.SetContentLengthHeader();
                            }

                            pereq.RequestContentStream = contentStream;
                            asyncResult = BaseAsyncResult.InvokeAsync(requestMessage.BeginGetRequestStream, this.AsyncEndGetRequestStream, asyncStateBag);
                        }

                        pereq.SetRequestCompletedSynchronously(asyncResult.CompletedSynchronously);
                        this.SetCompletedSynchronously(pereq.RequestCompletedSynchronously);
                    }
                    else
                    {
                        this.SetCompleted();

                        if (this.CompletedSynchronously)
                        {
                            this.HandleCompleted(pereq);
                        }
                    }
                }
                catch (InvalidOperationException e)
                {
                    e = WebUtil.GetHttpWebResponse(e, ref responseMsg);
                    this.HandleOperationException(e, responseMsg);
                    this.HandleCompleted(pereq);
                }
                finally
                {
                    WebUtil.DisposeMessage(responseMsg);
                }

                // If the current request is completed synchronously, we need to call FinishCurrentChange() to process the response payload.
                // FinishCurrentChange() will not call BeginCreateNextChange() when the request is synchronous.
                // If the current request is completed asynchronously, an async thread will call FinishCurrentChange() to process the response payload.
                // FinishCurrentchange() will then call BeginCreateNextChange() from the async thread and we need to exit this loop.
                // If requestMessage = this.CreateNextRequest() returns null, we would have called this.SetCompleted() above and this.IsCompletedInternally
                // would be true. This means we are done processing all changed entries and we should not call this.FinishCurrentChange().
                if (pereq != null && pereq.RequestCompleted && pereq.RequestCompletedSynchronously && !this.IsCompletedInternally)
                {
                    Debug.Assert(requestMessage != null, "httpWebRequest != null");
                    this.FinishCurrentChange(pereq);
                }

                // In the condition for the do-while loop we must test for pereq.RequestCompletedSynchronously and not asyncResult.CompletedSynchronously.
                // pereq.RequestCompletedSynchronously is true only if all async calls completed synchronously.  If we don't exit this loop when
                // pereq.RequestCompletedSynchronously is false, the current thread and an async thread will both re-enter BeginCreateNextChange()
                // and we will fail.  We can only process one request at a given time.
            }while (((pereq == null) || (pereq.RequestCompleted && pereq.RequestCompletedSynchronously)) && !this.IsCompletedInternally);
            Debug.Assert((this.CompletedSynchronously && this.IsCompleted) || !this.CompletedSynchronously, "sync without complete");
            Debug.Assert(this.entryIndex < this.ChangedEntries.Count || this.ChangedEntries.All(o => o.ContentGeneratedForSave), "didn't generate content for all entities/links");
        }
예제 #16
0
        /// <summary>
        /// Check to see if the resource to be inserted is a media descriptor, and if so
        /// setup a POST request for the media content first and turn the rest of
        /// the operation into a PUT to update the rest of the properties.
        /// </summary>
        /// <param name="entityDescriptor">The resource to check/process</param>
        /// <returns>An instance of ODataRequestMessage to do POST to the media resource</returns>
        private ODataRequestMessageWrapper CheckAndProcessMediaEntryPost(EntityDescriptor entityDescriptor)
        {
            // TODO: Revisit the design of how media link entries are handled during update
            ClientEdmModel       model = this.RequestInfo.Model;
            ClientTypeAnnotation type  = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));

            if (!type.IsMediaLinkEntry && !entityDescriptor.IsMediaLinkEntry)
            {
                // this is not a media link descriptor, process normally
                return(null);
            }

            if (type.MediaDataMember == null && entityDescriptor.SaveStream == null)
            {
                // The entity is marked as MLE but we don't have the content property
                //   and the user didn't set the save stream.
                throw Error.InvalidOperation(Strings.Context_MLEWithoutSaveStream(type.ElementTypeName));
            }

            Debug.Assert(
                (type.MediaDataMember != null && entityDescriptor.SaveStream == null) ||
                (type.MediaDataMember == null && entityDescriptor.SaveStream != null),
                "Only one way of specifying the MR content is allowed.");

            ODataRequestMessageWrapper mediaRequest = null;

            if (type.MediaDataMember != null)
            {
                string contentType   = null;
                int    contentLength = 0;

                if (type.MediaDataMember.MimeTypeProperty == null)
                {
                    contentType = XmlConstants.MimeApplicationOctetStream;
                }
                else
                {
                    object mimeTypeValue = type.MediaDataMember.MimeTypeProperty.GetValue(entityDescriptor.Entity);
                    String mimeType      = mimeTypeValue != null?mimeTypeValue.ToString() : null;

                    if (String.IsNullOrEmpty(mimeType))
                    {
                        throw Error.InvalidOperation(
                                  Strings.Context_NoContentTypeForMediaLink(
                                      type.ElementTypeName,
                                      type.MediaDataMember.MimeTypeProperty.PropertyName));
                    }

                    contentType = mimeType;
                }

                object value = type.MediaDataMember.GetValue(entityDescriptor.Entity);
                if (value == null)
                {
                    this.mediaResourceRequestStream = null;
                }
                else
                {
                    byte[] buffer = value as byte[];
                    if (buffer == null)
                    {
                        string   mime;
                        Encoding encoding;
                        ContentTypeUtil.ReadContentType(contentType, out mime, out encoding);

                        if (encoding == null)
                        {
                            encoding     = Encoding.UTF8;
                            contentType += XmlConstants.MimeTypeUtf8Encoding;
                        }

                        buffer = encoding.GetBytes(ClientConvert.ToString(value));
                    }

                    contentLength = buffer.Length;

                    // Need to specify that the buffer is publicly visible as we need to access it later on
                    this.mediaResourceRequestStream = new MemoryStream(buffer, 0, buffer.Length, false, true);
                }

                HeaderCollection headers = new HeaderCollection();
                headers.SetHeader(XmlConstants.HttpContentLength, contentLength.ToString(CultureInfo.InvariantCulture));
                headers.SetHeader(XmlConstants.HttpContentType, contentType);

                mediaRequest = this.CreateMediaResourceRequest(
                    entityDescriptor.GetResourceUri(this.RequestInfo.BaseUriResolver, false /*queryLink*/),
                    XmlConstants.HttpMethodPost,
                    Util.ODataVersion4,
                    type.MediaDataMember == null, // sendChunked
                    true,                         // applyResponsePreference
                    headers,
                    entityDescriptor);
            }
            else
            {
                HeaderCollection headers = new HeaderCollection();
                this.SetupMediaResourceRequest(headers, entityDescriptor.SaveStream, null /*etag*/);

                mediaRequest = this.CreateMediaResourceRequest(
                    entityDescriptor.GetResourceUri(this.RequestInfo.BaseUriResolver, false /*queryLink*/),
                    XmlConstants.HttpMethodPost,
                    Util.ODataVersion4,
                    type.MediaDataMember == null, // sendChunked
                    true,                         // applyResponsePreference
                    headers,
                    entityDescriptor);
            }

            // Convert the insert into an update for the media link descriptor we just created
            // (note that the identity still needs to be fixed up on the resbox once
            // the response comes with the 'location' header; that happens during processing
            // of the response in SavedResource())
            entityDescriptor.State = EntityStates.Modified;

            return(mediaRequest);
        }
예제 #17
0
 /// <summary>
 /// Creates a request message with the given arguments.
 /// </summary>
 /// <param name="requestMessageArgs">The request message args.</param>
 /// <returns>Newly created request message.</returns>
 internal ODataRequestMessageWrapper CreateRequestMessage(BuildingRequestEventArgs requestMessageArgs)
 {
     return(ODataRequestMessageWrapper.CreateRequestMessageWrapper(requestMessageArgs, this.requestInfo));
 }
예제 #18
0
 /// <summary>
 /// This method wraps the HttpWebRequest.EndGetResponse method call. The reason for doing this is to give us a place
 /// to invoke internal test hook callbacks that can validate the response headers.
 /// </summary>
 /// <param name="request">HttpWebRequest instance</param>
 /// <param name="asyncResult">Async result obtained from previous call to BeginGetResponse.</param>
 /// <returns>Returns the HttpWebResponse from the wrapped EndGetResponse method.</returns>
 internal IODataResponseMessage EndGetResponse(ODataRequestMessageWrapper request, IAsyncResult asyncResult)
 {
     return this.Context.EndGetResponse(request, asyncResult);
 }
예제 #19
0
 /// <summary>constructor</summary>
 /// <param name="source">source object of async request</param>
 /// <param name="method">async method name on source object</param>
 /// <param name="serviceRequest">Originating serviceRequest</param>
 /// <param name="request">Originating WebRequest</param>
 /// <param name="requestInfo">The request info of the originating request.</param>
 /// <param name="callback">user callback</param>
 /// <param name="state">user state</param>
 /// <param name="requestContentStream">the stream containing the request data.</param>
 internal QueryResult(object source, string method, DataServiceRequest serviceRequest, ODataRequestMessageWrapper request, RequestInfo requestInfo, AsyncCallback callback, object state, ContentStream requestContentStream)
     : this(source, method, serviceRequest, request, requestInfo, callback, state)
 {
     Debug.Assert(null != requestContentStream, "null requestContentStream");
     this.requestContentStream = requestContentStream;
 }
예제 #20
0
        /// <summary>
        /// Creates an instance of ODataMessageWriter.
        /// </summary>
        /// <param name="requestMessage">Instance of IODataRequestMessage.</param>
        /// <param name="requestInfo">RequestInfo containing information about the client settings.</param>
        /// <param name="isParameterPayload">true if the writer is intended to for a parameter payload, false otherwise.</param>
        /// <returns>An instance of ODataMessageWriter.</returns>
        internal static ODataMessageWriter CreateMessageWriter(ODataRequestMessageWrapper requestMessage, RequestInfo requestInfo, bool isParameterPayload)
        {
            var writerSettings = requestInfo.WriteHelper.CreateSettings(requestMessage.IsBatchPartRequest, requestInfo.Context.EnableWritingODataAnnotationWithoutPrefix);

            return(requestMessage.CreateWriter(writerSettings, isParameterPayload));
        }
예제 #21
0
 /// <summary>constructor</summary>
 /// <param name="source">source object of async request</param>
 /// <param name="method">async method name on source object</param>
 /// <param name="serviceRequest">Originating serviceRequest</param>
 /// <param name="request">Originating WebRequest</param>
 /// <param name="requestInfo">The request info of the originating request.</param>
 /// <param name="callback">user callback</param>
 /// <param name="state">user state</param>
 internal QueryResult(object source, string method, DataServiceRequest serviceRequest, ODataRequestMessageWrapper request, RequestInfo requestInfo, AsyncCallback callback, object state)
     : base(source, method, callback, state)
 {
     Debug.Assert(null != request, "null request");
     this.ServiceRequest = serviceRequest;
     this.Request        = request;
     this.RequestInfo    = requestInfo;
     this.Abortable      = request;
 }
예제 #22
0
        /// <summary>
        /// Writes the body operation parameters associated with a ServiceAction. For each BodyOperationParameter:
        /// 1. calls ODataPropertyConverter  to convert CLR object into ODataValue/primitive values.
        /// 2. then calls ODataParameterWriter to write the ODataValue/primitive values.
        /// </summary>
        /// <param name="operationParameters">The list of operation parameters to write.</param>
        /// <param name="requestMessage">The OData request message used to write the operation parameters.</param>
        internal void WriteBodyOperationParameters(List <BodyOperationParameter> operationParameters, ODataRequestMessageWrapper requestMessage)
        {
            Debug.Assert(requestMessage != null, "requestMessage != null");
            Debug.Assert(operationParameters != null, "operationParameters != null");
            Debug.Assert(operationParameters.Any(), "operationParameters.Any()");

            using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, true /*isParameterPayload*/))
            {
                ODataParameterWriter parameterWriter = messageWriter.CreateODataParameterWriter(null);
                parameterWriter.WriteStart();

                foreach (BodyOperationParameter operationParameter in operationParameters)
                {
                    if (operationParameter.Value == null)
                    {
                        parameterWriter.WriteValue(operationParameter.Name, operationParameter.Value);
                    }
                    else
                    {
                        ClientEdmModel model   = this.requestInfo.Model;
                        IEdmType       edmType = model.GetOrCreateEdmType(operationParameter.Value.GetType());
                        Debug.Assert(edmType != null, "edmType != null");

                        switch (edmType.TypeKind)
                        {
                        case EdmTypeKind.Collection:
                        {
                            this.WriteCollectionValueInBodyOperationParameter(parameterWriter, operationParameter, (IEdmCollectionType)edmType);
                            break;
                        }

                        case EdmTypeKind.Complex:
                        case EdmTypeKind.Entity:
                        {
                            Debug.Assert(model.GetClientTypeAnnotation(edmType).ElementType != null, "model.GetClientTypeAnnotation(edmType).ElementType != null");
                            ODataResourceWrapper entry = this.CreateODataResourceFromEntityOperationParameter(model.GetClientTypeAnnotation(edmType), operationParameter.Value);
                            Debug.Assert(entry != null, "entry != null");
                            var entryWriter = parameterWriter.CreateResourceWriter(operationParameter.Name);
                            ODataWriterHelper.WriteResource(entryWriter, entry);
                            break;
                        }

                        case EdmTypeKind.Primitive:
                            object primitiveValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(operationParameter.Value, operationParameter.Value.GetType());
                            parameterWriter.WriteValue(operationParameter.Name, primitiveValue);
                            break;

                        case EdmTypeKind.Enum:
                            ODataEnumValue tmp = this.propertyConverter.CreateODataEnumValue(
                                model.GetClientTypeAnnotation(edmType).ElementType,
                                operationParameter.Value,
                                false);
                            parameterWriter.WriteValue(operationParameter.Name, tmp);

                            break;

                        default:
                            // EdmTypeKind.Row
                            // EdmTypeKind.EntityReference
                            throw new NotSupportedException(Strings.Serializer_InvalidParameterType(operationParameter.Name, edmType.TypeKind));
                        }
                    } // else
                }     // foreach

                parameterWriter.WriteEnd();
                parameterWriter.Flush();
            }
        }
 /// <summary>
 /// This method wraps the HttpWebRequest.EndGetResponse method call. The reason for doing this is to give us a place
 /// to invoke internal test hook callbacks that can validate the response headers.
 /// </summary>
 /// <param name="request">HttpWebRequest instance</param>
 /// <param name="asyncResult">Async result obtained from previous call to BeginGetResponse.</param>
 /// <returns>Returns the HttpWebResponse from the wrapped EndGetResponse method.</returns>
 internal IODataResponseMessage EndGetResponse(ODataRequestMessageWrapper request, IAsyncResult asyncResult)
 {
     return(this.Context.EndGetResponse(request, asyncResult));
 }
예제 #24
0
        /// <summary>
        /// Write the entry element.
        /// </summary>
        /// <param name="entityDescriptor">The entity.</param>
        /// <param name="relatedLinks">Collection of links related to the entity.</param>
        /// <param name="requestMessage">The OData request message.</param>
        internal void WriteEntry(EntityDescriptor entityDescriptor, IEnumerable <LinkDescriptor> relatedLinks, ODataRequestMessageWrapper requestMessage)
        {
            ClientEdmModel       model      = this.requestInfo.Model;
            ClientTypeAnnotation entityType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));

            using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, false /*isParameterPayload*/))
            {
                ODataWriterWrapper entryWriter = ODataWriterWrapper.CreateForEntry(messageWriter, this.requestInfo.Configurations.RequestPipeline);

                // Get the server type name using the type resolver or from the entity descriptor
                string serverTypeName = this.requestInfo.GetServerTypeName(entityDescriptor);

                var entry = CreateODataEntry(entityDescriptor, serverTypeName, entityType, this.requestInfo.Format);
                if (serverTypeName == null)
                {
                    serverTypeName = this.requestInfo.InferServerTypeNameFromServerModel(entityDescriptor);
                }

                IEnumerable <ClientPropertyAnnotation> properties;
                if ((!Util.IsFlagSet(this.options, SaveChangesOptions.ReplaceOnUpdate) &&
                     entityDescriptor.State == EntityStates.Modified &&
                     entityDescriptor.PropertiesToSerialize.Any()) ||
                    (Util.IsFlagSet(this.options, SaveChangesOptions.PostOnlySetProperties) &&
                     entityDescriptor.State == EntityStates.Added))
                {
                    properties = entityType.PropertiesToSerialize().Where(prop => entityDescriptor.PropertiesToSerialize.Contains(prop.PropertyName));
                }
                else
                {
                    properties = entityType.PropertiesToSerialize();
                }

                entry.Properties = this.propertyConverter.PopulateProperties(entityDescriptor.Entity, serverTypeName, properties);

                entryWriter.WriteStart(entry, entityDescriptor.Entity);

                this.WriteNestedComplexProperties(entityDescriptor.Entity, serverTypeName, properties, entryWriter);

                if (EntityStates.Added == entityDescriptor.State)
                {
                    this.WriteNestedResourceInfo(entityDescriptor, relatedLinks, entryWriter);
                }

                entryWriter.WriteEnd(entry, entityDescriptor.Entity);
            }
        }
예제 #25
0
        /// <summary>
        /// Generate the batch request for all changes to save.
        /// </summary>
        /// <returns>Returns the instance of ODataRequestMessage containing all the headers and payload for the batch request.</returns>
        private ODataRequestMessageWrapper GenerateBatchRequest()
        {
            if (this.ChangedEntries.Count == 0 && this.Queries == null)
            {
                this.SetCompleted();
                return(null);
            }

            ODataRequestMessageWrapper batchRequestMessage = this.CreateBatchRequest();

            // we need to fire request after the headers have been written, but before we write the payload
            batchRequestMessage.FireSendingRequest2(null);

            using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(batchRequestMessage, this.RequestInfo, false /*isParameterPayload*/))
            {
                this.batchWriter = messageWriter.CreateODataBatchWriter();
                this.batchWriter.WriteStartBatch();

                if (this.Queries != null)
                {
                    foreach (DataServiceRequest query in this.Queries)
                    {
                        QueryComponents queryComponents = query.QueryComponents(this.RequestInfo.Model);
                        Uri             requestUri      = this.RequestInfo.BaseUriResolver.GetOrCreateAbsoluteUri(queryComponents.Uri);

                        Debug.Assert(requestUri != null, "request uri is null");
                        Debug.Assert(requestUri.IsAbsoluteUri, "request uri is not absolute uri");

                        HeaderCollection headers = new HeaderCollection();

                        headers.SetRequestVersion(queryComponents.Version, this.RequestInfo.MaxProtocolVersionAsVersion);

                        this.RequestInfo.Format.SetRequestAcceptHeaderForQuery(headers, queryComponents);

                        ODataRequestMessageWrapper batchOperationRequestMessage = this.CreateRequestMessage(XmlConstants.HttpMethodGet, requestUri, headers, this.RequestInfo.HttpStack, null /*descriptor*/, null /*contentId*/);

                        batchOperationRequestMessage.FireSendingEventHandlers(null /*descriptor*/);
                    }
                }
                else if (0 < this.ChangedEntries.Count)
                {
                    if (Util.IsBatchWithSingleChangeset(this.Options))
                    {
                        this.batchWriter.WriteStartChangeset();
                    }

                    var model = this.RequestInfo.Model;

                    for (int i = 0; i < this.ChangedEntries.Count; ++i)
                    {
                        if (Util.IsBatchWithIndependentOperations(this.Options))
                        {
                            this.batchWriter.WriteStartChangeset();
                        }

                        Descriptor descriptor = this.ChangedEntries[i];
                        if (descriptor.ContentGeneratedForSave)
                        {
                            continue;
                        }

                        EntityDescriptor entityDescriptor = descriptor as EntityDescriptor;
                        if (descriptor.DescriptorKind == DescriptorKind.Entity)
                        {
                            if (entityDescriptor.State == EntityStates.Added)
                            {
                                // We don't support adding MLE/MR in batch mode
                                ClientTypeAnnotation type = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));
                                if (type.IsMediaLinkEntry || entityDescriptor.IsMediaLinkEntry)
                                {
                                    throw Error.NotSupported(Strings.Context_BatchNotSupportedForMediaLink);
                                }
                            }
                            else if (entityDescriptor.State == EntityStates.Unchanged || entityDescriptor.State == EntityStates.Modified)
                            {
                                // We don't support PUT for the MR in batch mode
                                // It's OK to PUT the MLE alone inside a batch mode though
                                if (entityDescriptor.SaveStream != null)
                                {
                                    throw Error.NotSupported(Strings.Context_BatchNotSupportedForMediaLink);
                                }
                            }
                        }
                        else if (descriptor.DescriptorKind == DescriptorKind.NamedStream)
                        {
                            // Similar to MR, we do not support adding named streams in batch mode.
                            throw Error.NotSupported(Strings.Context_BatchNotSupportedForNamedStreams);
                        }

                        ODataRequestMessageWrapper operationRequestMessage;
                        if (descriptor.DescriptorKind == DescriptorKind.Entity)
                        {
                            operationRequestMessage = this.CreateRequest(entityDescriptor);
                        }
                        else
                        {
                            operationRequestMessage = this.CreateRequest((LinkDescriptor)descriptor);
                        }

                        // we need to fire request after the headers have been written, but before we write the payload
                        operationRequestMessage.FireSendingRequest2(descriptor);

                        this.CreateChangeData(i, operationRequestMessage);

                        if (Util.IsBatchWithIndependentOperations(this.Options))
                        {
                            this.batchWriter.WriteEndChangeset();
                        }
                    }

                    if (Util.IsBatchWithSingleChangeset(this.Options))
                    {
                        this.batchWriter.WriteEndChangeset();
                    }
                }

                this.batchWriter.WriteEndBatch();
                this.batchWriter.Flush();
            }

            Debug.Assert(this.ChangedEntries.All(o => o.ContentGeneratedForSave), "didn't generated content for all entities/links");
            return(batchRequestMessage);
        }
예제 #26
0
 /// <summary>
 /// Writes an entity reference link.
 /// </summary>
 /// <param name="binding">The link descriptor.</param>
 /// <param name="requestMessage">The request message used for writing the payload.</param>
 /// <param name="isBatch">True if batch, false otherwise.</param>
 internal void WriteEntityReferenceLink(LinkDescriptor binding, ODataRequestMessageWrapper requestMessage, bool isBatch)
예제 #27
0
 /// <summary>
 /// Creates an instance of ODataMessageWriter.
 /// </summary>
 /// <param name="requestMessage">Instance of IODataRequestMessage.</param>
 /// <param name="requestInfo">RequestInfo containing information about the client settings.</param>
 /// <param name="isParameterPayload">true if the writer is intended to for a parameter payload, false otherwise.</param>
 /// <returns>An instance of ODataMessageWriter.</returns>
 internal static ODataMessageWriter CreateMessageWriter(ODataRequestMessageWrapper requestMessage, RequestInfo requestInfo, bool isParameterPayload)
 {
     var writerSettings = requestInfo.WriteHelper.CreateSettings(requestMessage.IsBatchPartRequest, requestInfo.Context.EnableAtom, requestInfo.Context.ODataSimplified);
     return requestMessage.CreateWriter(writerSettings, isParameterPayload);
 }
예제 #28
0
        /// <summary>
        /// Writes the body operation parameters associated with a ServiceAction. For each BodyOperationParameter:
        /// 1. calls ODataPropertyConverter  to convert CLR object into ODataValue/primitive values.
        /// 2. then calls ODataParameterWriter to write the ODataValue/primitive values.
        /// </summary>
        /// <param name="operationParameters">The list of operation parameters to write.</param>
        /// <param name="requestMessage">The OData request message used to write the operation parameters.</param>
        internal void WriteBodyOperationParameters(List <BodyOperationParameter> operationParameters, ODataRequestMessageWrapper requestMessage)
        {
            Debug.Assert(requestMessage != null, "requestMessage != null");
            Debug.Assert(operationParameters != null, "operationParameters != null");
            Debug.Assert(operationParameters.Any(), "operationParameters.Any()");

            using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, true /*isParameterPayload*/))
            {
                ODataParameterWriter parameterWriter = messageWriter.CreateODataParameterWriter((IEdmOperation)null);
                parameterWriter.WriteStart();

                foreach (OperationParameter operationParameter in operationParameters)
                {
                    if (operationParameter.Value == null)
                    {
                        parameterWriter.WriteValue(operationParameter.Name, operationParameter.Value);
                    }
                    else
                    {
                        ClientEdmModel model   = this.requestInfo.Model;
                        IEdmType       edmType = model.GetOrCreateEdmType(operationParameter.Value.GetType());
                        Debug.Assert(edmType != null, "edmType != null");

                        switch (edmType.TypeKind)
                        {
                        case EdmTypeKind.Collection:
                        {
                            // TODO: just call ODataPropertyConverter.CreateODataCollection()
                            IEnumerator           enumerator           = ((ICollection)operationParameter.Value).GetEnumerator();
                            ODataCollectionWriter collectionWriter     = parameterWriter.CreateCollectionWriter(operationParameter.Name);
                            ODataCollectionStart  odataCollectionStart = new ODataCollectionStart();
                            collectionWriter.WriteStart(odataCollectionStart);

                            while (enumerator.MoveNext())
                            {
                                Object collectionItem = enumerator.Current;
                                if (collectionItem == null)
                                {
                                    throw new NotSupportedException(Strings.Serializer_NullCollectionParamterItemValue(operationParameter.Name));
                                }

                                IEdmType edmItemType = model.GetOrCreateEdmType(collectionItem.GetType());
                                Debug.Assert(edmItemType != null, "edmItemType != null");

                                switch (edmItemType.TypeKind)
                                {
                                case EdmTypeKind.Complex:
                                {
                                    Debug.Assert(model.GetClientTypeAnnotation(edmItemType).ElementType != null, "edmItemType.GetClientTypeAnnotation().ElementType != null");
                                    ODataComplexValue complexValue = this.propertyConverter.CreateODataComplexValue(
                                        model.GetClientTypeAnnotation(edmItemType).ElementType,
                                        collectionItem,
                                        null /*propertyName*/,
                                        false /*isCollectionItem*/,
                                        null /*visitedComplexTypeObjects*/);

                                    Debug.Assert(complexValue != null, "complexValue != null");
                                    collectionWriter.WriteItem(complexValue);
                                    break;
                                }

                                case EdmTypeKind.Primitive:
                                {
                                    object primitiveItemValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(collectionItem, collectionItem.GetType());
                                    collectionWriter.WriteItem(primitiveItemValue);
                                    break;
                                }

                                case EdmTypeKind.Enum:
                                {
                                    ODataEnumValue enumTmp = this.propertyConverter.CreateODataEnumValue(
                                        model.GetClientTypeAnnotation(edmItemType).ElementType,
                                        collectionItem,
                                        false);
                                    collectionWriter.WriteItem(enumTmp);
                                    break;
                                }

                                default:

                                    // EdmTypeKind.Entity
                                    // EdmTypeKind.Row
                                    // EdmTypeKind.EntityReference
                                    throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(operationParameter.Name, edmItemType.TypeKind));
                                }
                            }

                            collectionWriter.WriteEnd();
                            collectionWriter.Flush();
                            break;
                        }

                        case EdmTypeKind.Complex:
                        {
                            Debug.Assert(model.GetClientTypeAnnotation(edmType).ElementType != null, "model.GetClientTypeAnnotation(edmType).ElementType != null");
                            ODataComplexValue complexValue = this.propertyConverter.CreateODataComplexValue(
                                model.GetClientTypeAnnotation(edmType).ElementType,
                                operationParameter.Value,
                                null /*propertyName*/,
                                false /*isCollectionItemType*/,
                                null /*visitedComplexTypeObjects*/);

                            Debug.Assert(complexValue != null, "complexValue != null");
                            parameterWriter.WriteValue(operationParameter.Name, complexValue);
                            break;
                        }

                        case EdmTypeKind.Primitive:
                            object primitiveValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(operationParameter.Value, operationParameter.Value.GetType());
                            parameterWriter.WriteValue(operationParameter.Name, primitiveValue);
                            break;

                        case EdmTypeKind.Enum:
                            ODataEnumValue tmp = this.propertyConverter.CreateODataEnumValue(
                                model.GetClientTypeAnnotation(edmType).ElementType,
                                operationParameter.Value,
                                false);
                            parameterWriter.WriteValue(operationParameter.Name, tmp);

                            break;

                        default:
                            // EdmTypeKind.Entity
                            // EdmTypeKind.Row
                            // EdmTypeKind.EntityReference
                            throw new NotSupportedException(Strings.Serializer_InvalidParameterType(operationParameter.Name, edmType.TypeKind));
                        }
                    } // else
                }     // foreach

                parameterWriter.WriteEnd();
                parameterWriter.Flush();
            }
        }
예제 #29
0
        /// <summary>
        /// Write the entry element.
        /// </summary>
        /// <param name="entityDescriptor">The entity.</param>
        /// <param name="relatedLinks">Collection of links related to the entity.</param>
        /// <param name="requestMessage">The OData request message.</param>
        internal void WriteEntry(EntityDescriptor entityDescriptor, IEnumerable<LinkDescriptor> relatedLinks, ODataRequestMessageWrapper requestMessage)
        {
            ClientEdmModel model = this.requestInfo.Model;
            ClientTypeAnnotation entityType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));
            using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, false /*isParameterPayload*/))
            {
                ODataWriterWrapper entryWriter = ODataWriterWrapper.CreateForEntry(messageWriter, this.requestInfo.Configurations.RequestPipeline);

                // Get the server type name using the type resolver or from the entity descriptor
                string serverTypeName = this.requestInfo.GetServerTypeName(entityDescriptor);

                var entry = CreateODataEntry(entityDescriptor, serverTypeName, entityType, this.requestInfo.Format);
                if (serverTypeName == null)
                {
                    serverTypeName = this.requestInfo.InferServerTypeNameFromServerModel(entityDescriptor);
                }

                IEnumerable<ClientPropertyAnnotation> properties;
                if ((!Util.IsFlagSet(this.options, SaveChangesOptions.ReplaceOnUpdate) &&
                    entityDescriptor.State == EntityStates.Modified &&
                    entityDescriptor.PropertiesToSerialize.Any()) ||
                    (Util.IsFlagSet(this.options, SaveChangesOptions.PostOnlySetProperties) &&
                    entityDescriptor.State == EntityStates.Added))
                {
                    properties = entityType.PropertiesToSerialize().Where(prop => entityDescriptor.PropertiesToSerialize.Contains(prop.PropertyName));
                }
                else
                {
                    properties = entityType.PropertiesToSerialize();
                }

                entry.Properties = this.propertyConverter.PopulateProperties(entityDescriptor.Entity, serverTypeName, properties);

                entryWriter.WriteStart(entry, entityDescriptor.Entity);

                if (EntityStates.Added == entityDescriptor.State)
                {
                    this.WriteNavigationLink(entityDescriptor, relatedLinks, entryWriter);
                }

                entryWriter.WriteEnd(entry, entityDescriptor.Entity);
            }
        }
예제 #30
0
 /// <summary>constructor</summary>
 /// <param name="entity">entity</param>
 /// <param name="propertyName">name of collection or reference property to load</param>
 /// <param name="context">Originating context</param>
 /// <param name="request">Originating WebRequest</param>
 /// <param name="callback">user callback</param>
 /// <param name="state">user state</param>
 /// <param name="dataServiceRequest">request object.</param>
 /// <param name="plan">Projection plan for materialization; possibly null.</param>
 /// <param name="isContinuation">Whether this request is a continuation request.</param>
 internal LoadPropertyResult(object entity, string propertyName, DataServiceContext context, ODataRequestMessageWrapper request, AsyncCallback callback, object state, DataServiceRequest dataServiceRequest, ProjectionPlan plan, bool isContinuation)
     : base(context, Util.LoadPropertyMethodName, dataServiceRequest, request, new RequestInfo(context, isContinuation), callback, state)
 {
     this.entity       = entity;
     this.propertyName = propertyName;
     this.plan         = plan;
 }
예제 #31
0
        /// <summary>
        /// Writes an entity reference link.
        /// </summary>
        /// <param name="binding">The link descriptor.</param>
        /// <param name="requestMessage">The request message used for writing the payload.</param>
        internal void WriteEntityReferenceLink(LinkDescriptor binding, ODataRequestMessageWrapper requestMessage)
#endif
        {
            using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, false /*isParameterPayload*/))
            {
                EntityDescriptor targetResource = this.requestInfo.EntityTracker.GetEntityDescriptor(binding.Target);

                Uri targetReferenceLink = targetResource.GetLatestIdentity();

                if (targetReferenceLink == null)
                {
#if DEBUG
                    Debug.Assert(isBatch, "we should be cross-referencing entities only in batch scenarios");
#endif
                    targetReferenceLink = UriUtil.CreateUri("$" + targetResource.ChangeOrder.ToString(CultureInfo.InvariantCulture), UriKind.Relative);
                }

                ODataEntityReferenceLink referenceLink = new ODataEntityReferenceLink();
                referenceLink.Url = targetReferenceLink;
                messageWriter.WriteEntityReferenceLink(referenceLink);
            }
        }
예제 #32
0
        /// <summary>
        /// Creates an instance of ODataMessageWriter.
        /// </summary>
        /// <param name="requestMessage">Instance of IODataRequestMessage.</param>
        /// <param name="requestInfo">RequestInfo containing information about the client settings.</param>
        /// <param name="isParameterPayload">true if the writer is intended to for a parameter payload, false otherwise.</param>
        /// <returns>An instance of ODataMessageWriter.</returns>
        internal static ODataMessageWriter CreateMessageWriter(ODataRequestMessageWrapper requestMessage, RequestInfo requestInfo, bool isParameterPayload)
        {
            var writerSettings = requestInfo.WriteHelper.CreateSettings(requestMessage.IsBatchPartRequest, requestInfo.Context.EnableAtom, requestInfo.Context.ODataSimplified);

            return(requestMessage.CreateWriter(writerSettings, isParameterPayload));
        }