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