Example #1
0
 /// <summary>
 /// This is a common place where a real id is assigned to all client inserts.
 /// </summary>
 /// <param name="clientUploads">Collection of entities to check</param>
 private static void AssignRealIdsForClientInserts(IList <IOfflineEntity> clientUploads)
 {
     // Iterate over entities that dont have a ID which means its an client insert
     foreach (IOfflineEntity entity in clientUploads.Where(e => string.IsNullOrEmpty(e.ServiceMetadata.Id)))
     {
         entity.ServiceMetadata.Id = WebUtil.GenerateOfflineEntityId(entity);
     }
 }
        // This method ensures that we have a valid feed through the formatters. The BodyWriter delegate in WCF
        // seems to not recover from an unhandled exception caused by the formatters and sends out an empty response to the caller.
        // This is a workaround for this issue until we find a better solution.
        private SyncWriter GetSyncWriterWithContents()
        {
            // Get the appropriate SyncWriter instance based on the serialization format.
            SyncWriter oDataWriter = WebUtil.GetSyncWriter(_responseSerializationFormat, _baseUri);

            oDataWriter.StartFeed(_getChangesResponse.IsLastBatch, _getChangesResponse.ServerBlob);

            // Write entities for response.
            foreach (var entity in _getChangesResponse.EntityList)
            {
                // Set the Id and add item with a null tempId.

                entity.ServiceMetadata.Id = WebUtil.GenerateOfflineEntityId(entity);

                oDataWriter.AddItem(entity, null /*tempId*/);
            }

            return(oDataWriter);
        }
Example #3
0
        // This method ensures that we have a valid feed through the formatters. The BodyWriter delegate in WCF
        // seems to not recover from an unhandled exception caused by the formatters and sends out an empty response to the caller.
        // This is a workaround for this issue until we find a better solution.
        private SyncWriter GetSyncWriterWithContents()
        {
            var conflictEntryKeys = new List <string>();
            var errorEntryKeys    = new List <string>();
            var primaryKeyToIncomingEntitiesMapping = new Dictionary <string, IOfflineEntity>();

            // Save the mapping between entity PK string -> entity
            foreach (var entity in _incomingEntities)
            {
                string primaryKey = ReflectionUtility.GetPrimaryKeyString(entity);
                if (primaryKeyToIncomingEntitiesMapping.ContainsKey(primaryKey))
                {
                    throw SyncServiceException.CreateInternalServerError(Strings.MultipleEntriesWithSamePrimaryKeyInIncomingRequest);
                }

                primaryKeyToIncomingEntitiesMapping.Add(primaryKey, entity);
            }

            if (_rejectedEntities != null)
            {
                foreach (var entity in _rejectedEntities.Keys)
                {
                    string primaryKey = ReflectionUtility.GetPrimaryKeyString(entity);
                    if (primaryKeyToIncomingEntitiesMapping.ContainsKey(primaryKey))
                    {
                        throw SyncServiceException.CreateInternalServerError(Strings.MultipleEntriesWithSamePrimaryKeyInIncomingRequest);
                    }

                    primaryKeyToIncomingEntitiesMapping.Add(primaryKey, entity);
                }
            }

            // Get the appropriate SyncWriter instance based on the serialization format.
            var oDataWriter = WebUtil.GetSyncWriter(_responseSerializationFormat, _baseUri);

            oDataWriter.StartFeed(_applyChangesResponse.IsLastBatch, _applyChangesResponse.ServerBlob);

            // Write conflict entities.
            foreach (var entity in _applyChangesResponse.Conflicts)
            {
                // Add the primary key string to the conflictEntryKey list.
                // The primary keys are the same for both Live and Losing entities.
                conflictEntryKeys.Add(ReflectionUtility.GetPrimaryKeyString(entity.LiveEntity));

                string tempId;

                // If the client change lost, then we need to set the Id property
                // only if the property was not null/empty in the incoming request.
                string entityId = WebUtil.GenerateOfflineEntityId(entity.LiveEntity);

                // Set the Id property of the Live entity (server's copy).
                entity.LiveEntity.ServiceMetadata.Id = entityId;

                // Set the Id property of the Losing entity to the incoming entity's Id value
                entity.LosingEntity.ServiceMetadata.Id = entityId;

                // get the original tempId. Null value is ok.
                _idToTempIdMapping.TryGetValue(entityId, out tempId);

                if (entity.Resolution == SyncConflictResolution.ServerWins)
                {
                    // The losing entity is the client's copy.

                    // When resolution is ServerWins, we only need to set the losing change tempId.
                    oDataWriter.AddConflictItem(entity.LiveEntity, null /*tempId*/, entity.LosingEntity, tempId, entity.Resolution);
                }
                // If the client change won, then just set the Id property since an insert would have succeeded.
                else
                {
                    // When resolution is ClientWins, we only need to set the LiveEntity tempId.
                    oDataWriter.AddConflictItem(entity.LiveEntity, tempId, entity.LosingEntity, null /* tempId */, entity.Resolution);
                }
            }

            // Write error entities.
            foreach (var syncError in _applyChangesResponse.Errors)
            {
                Debug.Assert(null != syncError.LiveEntity);
                Debug.Assert(null != syncError.ErrorEntity);

                string entityId = WebUtil.GenerateOfflineEntityId(syncError.LiveEntity);

                // Set the Id for Live and Losing entity.
                syncError.LiveEntity.ServiceMetadata.Id  = entityId;
                syncError.ErrorEntity.ServiceMetadata.Id = entityId;

                string primaryKeyString = ReflectionUtility.GetPrimaryKeyString(syncError.ErrorEntity);

                // Add the string to the error key list.
                errorEntryKeys.Add(primaryKeyString);

                string tempId;

                _idToTempIdMapping.TryGetValue(entityId, out tempId);

                oDataWriter.AddErrorItem(syncError.LiveEntity, syncError.ErrorEntity, tempId, syncError.Description);
            }

            // Write all the inserted records here by iterating over the _incomingNewInsertEntities list
            foreach (var entity in _incomingNewInsertEntities)
            {
                string entityTempId;

                // Get the tempId of the entity.
                _idToTempIdMapping.TryGetValue(WebUtil.GenerateOfflineEntityId(entity), out entityTempId);

                // Write the output to the SyncWriter.
                oDataWriter.AddItem(entity, entityTempId);
            }

            return(oDataWriter);
        }
Example #4
0
        /// <summary>
        /// Read and parse the incoming request stream for a POST request.
        /// </summary>
        private void ReadIncomingRequestStreamForPost()
        {
            if (null == _serviceHost.RequestStream || !_serviceHost.RequestStream.CanRead)
            {
                SyncTracer.Info("Request stream for HTTP POST is empty, null or cannot be read.");
                return;
            }

            try
            {
                var reader = WebUtil.GetSyncReader(_serviceHost.GetRequestContentSerializationFormat(),
                                                   _serviceHost.RequestStream,
                                                   _configuration.TypeToTableGlobalNameMapping.Keys.ToArray());

                reader.Start();

                while (reader.Next())
                {
                    switch (reader.ItemType)
                    {
                    case ReaderItemType.Entry:
                        IOfflineEntity entity = reader.GetItem();

                        if (entity.ServiceMetadata.IsTombstone)
                        {
                            if (String.IsNullOrEmpty(entity.ServiceMetadata.Id))
                            {
                                throw SyncServiceException.CreateBadRequestError(Strings.TombstoneEntityHasNoId);
                            }

                            WebUtil.ParseIdStringAndPopulateKeyFields(entity, _serviceHost.ServiceBaseUri);
                        }

                        _entityList.Add(entity);

                        bool hasTempId = false;
                        if (reader.HasTempId())
                        {
                            // Save the entity id to tempId mapping for use later when writing response.
                            _idToTempIdMapping.Add(WebUtil.GenerateOfflineEntityId(entity), reader.GetTempId());

                            hasTempId = true;
                        }

                        // Make sure, we have atleast one of Id or TempId
                        if (String.IsNullOrEmpty(entity.ServiceMetadata.Id) && !hasTempId)
                        {
                            throw SyncServiceException.CreateBadRequestError(Strings.BothIdAndTempIdAreMissing);
                        }

                        break;

                    case ReaderItemType.SyncBlob:
                        _syncBlob = reader.GetServerBlob();

                        break;
                    }
                }
            }
            catch (XmlException exception)
            {
                SyncTracer.Warning("XmlException: {0}", WebUtil.GetExceptionMessage(exception));

                throw SyncServiceException.CreateBadRequestError(Strings.BadRequestPayload);
            }
        }