/// <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; }
/// <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); }
/// <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); }
/// <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(); } }
/// <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)
/// <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)); }
/// <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; } }
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)); }
/// <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; }
/// <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); }
/// <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; }
/// <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 }
/// <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; }
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"); }
/// <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); }
/// <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)); }
/// <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); }
/// <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; }
/// <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)); }
/// <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; }
/// <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)); }
/// <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); } }
/// <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); }
/// <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); }
/// <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(); } }
/// <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); } }
/// <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); } }
/// <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)); }