Abstract class for SyncReader that individual format readers needs to extend
Inheritance: IDisposable
        private object ProcessUploadRequest(HttpWebRequest webRequest, CacheRequest request)
        {
            using (Stream memoryStream = new MemoryStream())
            {
                // Create a SyncWriter to write the contents
                this._syncWriter = new ODataAtomWriter(base.BaseUri);

                this._syncWriter.StartFeed(true, request.KnowledgeBlob ?? new byte[0]);

                foreach (IOfflineEntity entity in request.Changes)
                {
                    // Skip tombstones that dont have a ID element.
                    if (entity.ServiceMetadata.IsTombstone && string.IsNullOrEmpty(entity.ServiceMetadata.Id))
                    {
                        continue;
                    }

                    string tempId = null;

                    // Check to see if this is an insert. i.e ServiceMetadata.Id is null or empty
                    if (string.IsNullOrEmpty(entity.ServiceMetadata.Id))
                    {
                        if (TempIdToEntityMapping == null)
                        {
                            TempIdToEntityMapping = new Dictionary<string, IOfflineEntity>();
                        }
                        tempId = Guid.NewGuid().ToString();
                        TempIdToEntityMapping.Add(tempId, entity);
                    }

                    this._syncWriter.AddItem(entity, tempId);
                }

                this._syncWriter.WriteFeed(XmlWriter.Create(memoryStream));

                memoryStream.Flush();
                // Set the content length
                webRequest.ContentLength = memoryStream.Position;

                using (Stream requestStream = webRequest.GetRequestStream())
                {
                    CopyStreamContent(memoryStream, requestStream);

                    // Close the request stream
                    requestStream.Flush();
                    requestStream.Close();
                }
            }

            // Fire the Before request handler
            this.FirePreRequestHandler(webRequest);

            // Get the response
            HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();

            if (webResponse.StatusCode == HttpStatusCode.OK)
            {
                ChangeSetResponse changeSetResponse = new ChangeSetResponse();

                using (Stream responseStream = webResponse.GetResponseStream())
                {
                    // Create the SyncReader
                    this._syncReader = new ODataAtomReader(responseStream, this._knownTypes);

                    // Read the response
                    while (this._syncReader.Next())
                    {
                        switch (this._syncReader.ItemType)
                        {
                            case ReaderItemType.Entry:
                                IOfflineEntity entity = this._syncReader.GetItem();
                                IOfflineEntity ackedEntity = entity;
                                string tempId = null;

                                // If conflict only one temp ID should be set
                                if (this._syncReader.HasTempId() && this._syncReader.HasConflictTempId())
                                {
                                    throw new CacheControllerException(string.Format("Service returned a TempId '{0}' in both live and conflicting entities.",
                                                                       this._syncReader.GetTempId()));
                                }

                                // Validate the live temp ID if any, before adding anything to the offline context
                                if (this._syncReader.HasTempId())
                                {
                                    tempId = this._syncReader.GetTempId();
                                    CheckEntityServiceMetadataAndTempIds(TempIdToEntityMapping, entity, tempId, changeSetResponse);
                                }

                                //  If conflict
                                if (this._syncReader.HasConflict())
                                {
                                    Conflict conflict = this._syncReader.GetConflict();
                                    IOfflineEntity conflictEntity = (conflict is SyncConflict) ?
                                                                    ((SyncConflict)conflict).LosingEntity : ((SyncError)conflict).ErrorEntity;

                                    // Validate conflict temp ID if any
                                    if (this._syncReader.HasConflictTempId())
                                    {
                                        tempId = this._syncReader.GetConflictTempId();
                                        CheckEntityServiceMetadataAndTempIds(TempIdToEntityMapping, conflictEntity, tempId, changeSetResponse);
                                    }

                                    // Add conflict
                                    changeSetResponse.AddConflict(conflict);

                                    //
                                    // If there is a conflict and the tempId is set in the conflict entity then the client version lost the
                                    // conflict and the live entity is the server version (ServerWins)
                                    //
                                    if (this._syncReader.HasConflictTempId() && entity.ServiceMetadata.IsTombstone)
                                    {
                                        //
                                        // This is a ServerWins conflict, or conflict error. The winning version is a tombstone without temp Id
                                        // so there is no way to map the winning entity with a temp Id. The temp Id is in the conflict so we are
                                        // using the conflict entity, which has the PK, to build a tombstone entity used to update the offline context
                                        //
                                        // In theory, we should copy the service metadata but it is the same end result as the service fills in
                                        // all the properties in the conflict entity
                                        //

                                        // Add the conflict entity
                                        conflictEntity.ServiceMetadata.IsTombstone = true;
                                        ackedEntity = conflictEntity;
                                    }
                                }

                                // Add ackedEntity to storage. If ackedEntity is still equal to entity then add non-conflict entity.
                                if (!String.IsNullOrEmpty(tempId)) {
                                    changeSetResponse.AddUpdatedItem(ackedEntity);
                                }

                                break;
                            case ReaderItemType.SyncBlob:
                                changeSetResponse.ServerBlob = this._syncReader.GetServerBlob();
                                break;
                        }
                    }
                }

                if (TempIdToEntityMapping != null && TempIdToEntityMapping.Count != 0)
                {
                    // The client sent some inserts which werent ack'd by the service. Throw.
                    StringBuilder builder = new StringBuilder("Server did not acknowledge with a permanant Id for the following tempId's: ");
                    builder.Append(string.Join(",", TempIdToEntityMapping.Keys.ToArray()));
                    throw new CacheControllerException(builder.ToString());
                }

                this.FirePostResponseHandler(webResponse);

                webResponse.Close();

                return changeSetResponse;
            }
            else
            {
                throw new CacheControllerException(
                                    string.Format("Remote service returned error status. Status: {0}, Description: {1}",
                                    webResponse.StatusCode,
                                    webResponse.StatusDescription));
            }
        }
        private object ProcessDownloadRequest(HttpWebRequest webRequest, CacheRequest request)
        {
            using (Stream memoryStream = new MemoryStream())
            {

                // Create a SyncWriter to write the contents
                this._syncWriter = new ODataAtomWriter(base.BaseUri);

                this._syncWriter.StartFeed(true, request.KnowledgeBlob ?? new byte[0]);

                this._syncWriter.WriteFeed(XmlWriter.Create(memoryStream));
                memoryStream.Flush();

                webRequest.ContentLength = memoryStream.Position;
                Stream requestStream = webRequest.GetRequestStream();
                CopyStreamContent(memoryStream, requestStream);

                requestStream.Flush();
                requestStream.Close();

                // Fire the Before request handler
                this.FirePreRequestHandler(webRequest);
            }

            // Get the response
            HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();

            if (webResponse.StatusCode == HttpStatusCode.OK)
            {
                ChangeSet changeSet = new ChangeSet();

                using (Stream responseStream = webResponse.GetResponseStream())
                {
                    // Create the SyncReader
                    this._syncReader = new ODataAtomReader(responseStream, this._knownTypes);

                    // Read the response
                    while (this._syncReader.Next())
                    {
                        switch (this._syncReader.ItemType)
                        {
                            case ReaderItemType.Entry:
                                changeSet.AddItem(this._syncReader.GetItem());
                                break;
                            case ReaderItemType.SyncBlob:
                                changeSet.ServerBlob = this._syncReader.GetServerBlob();
                                break;
                            case ReaderItemType.HasMoreChanges:
                                changeSet.IsLastBatch = !this._syncReader.GetHasMoreChangesValue();
                                break;
                        }
                    }

                    this.FirePostResponseHandler(webResponse);
                }

                webResponse.Close();

                return changeSet;
            }
            else
            {
                throw new CacheControllerException(
                                    string.Format("Remote service returned error status. Status: {0}, Description: {1}",
                                    webResponse.StatusCode,
                                    webResponse.StatusDescription));
            }
        }