Пример #1
0
        /// <summary>
        /// Get the entity from the incoming request that matches the primary key string of the entity passed as a parameter.
        /// </summary>
        /// <param name="primaryKeyToIncomingEntitiesMapping">Dictionary of mapping between primary key and the actual entities from incoming request.</param>
        /// <param name="entity">Entity for which to search a match in the incoming request.</param>
        /// <param name="isConflict">Indicates if this is called during conflict processing. Used to select appropriate error messages.</param>
        /// <returns>Entity from the incoming request.</returns>
        private static IOfflineEntity GetEntityFromIncomingRequest(Dictionary <string, IOfflineEntity> primaryKeyToIncomingEntitiesMapping,
                                                                   IOfflineEntity entity,
                                                                   bool isConflict)
        {
            // find the actual entity from the input list.
            var entityListInRequest = primaryKeyToIncomingEntitiesMapping.Where(e =>
                                                                                e.Key.Equals(ReflectionUtility.GetPrimaryKeyString(entity), StringComparison.InvariantCultureIgnoreCase)).ToList();

            // If no match is found, then throw an error.
            if (0 == entityListInRequest.Count)
            {
                if (isConflict)
                {
                    throw SyncServiceException.CreateInternalServerError(Strings.ConflictEntityMissingInIncomingRequest);
                }

                throw SyncServiceException.CreateInternalServerError(Strings.ErrorEntityMissingInIncomingRequest);
            }

            // If the entity corresponding to the key is null, then throw an error.
            if (null == entityListInRequest[0].Value)
            {
                if (isConflict)
                {
                    throw SyncServiceException.CreateInternalServerError(Strings.ConflictEntityMissingInIncomingRequest);
                }

                throw SyncServiceException.CreateInternalServerError(Strings.ErrorEntityMissingInIncomingRequest);
            }

            return(entityListInRequest[0].Value);
        }
Пример #2
0
        /// <summary>
        /// Check Metadata
        /// </summary>
        private void CheckEntityServiceMetadataAndTempIds(AsyncArgsWrapper wrapper, IOfflineEntity entity, string tempId)
        {
            // Check service ID
            if (string.IsNullOrEmpty(entity.GetServiceMetadata().Id))
            {
                throw new CacheControllerException(
                          string.Format("Service did not return a permanent Id for tempId '{0}'", tempId));
            }

            // If an entity has a temp id then it should not be a tombstone
            if (entity.GetServiceMetadata().IsTombstone)
            {
                throw new CacheControllerException(string.Format(
                                                       "Service returned a tempId '{0}' in tombstoned entity.", tempId));
            }

            // Check that the tempId was sent by client
            if (!wrapper.TempIdToEntityMapping.ContainsKey(tempId))
            {
                throw new CacheControllerException(
                          "Service returned a response for a tempId which was not uploaded by the client. TempId: " + tempId);
            }

            // Once received, remove the tempId from the mapping list.
            wrapper.TempIdToEntityMapping.Remove(tempId);
        }
Пример #3
0
        private void CheckEntityServiceMetadataAndTempIds(Dictionary <string, IOfflineEntity> tempIdToEntityMapping,
                                                          IOfflineEntity entity, string tempId, ChangeSetResponse response)
        {
            // Check service ID
            if (string.IsNullOrEmpty(entity.ServiceMetadata.Id))
            {
                throw new CacheControllerException(string.Format("Service did not return a permanent Id for tempId '{0}'", tempId));
            }

            // If an entity has a temp id then it should not be a tombstone
            if (entity.ServiceMetadata.IsTombstone)
            {
                throw new CacheControllerException(string.Format("Service returned a tempId '{0}' in tombstoned entity.", tempId));
            }

            // Check that the tempId was sent by client
            if (!tempIdToEntityMapping.ContainsKey(tempId))
            {
                throw new CacheControllerException("Service returned a response for a tempId which was not uploaded by the client. TempId: " + tempId);
            }

            // Add the entity to the Updated list.
            response.AddUpdatedItem(entity);

            // Once received, remove the tempId from the mapping list.
            tempIdToEntityMapping.Remove(tempId);
        }
Пример #4
0
        /// <summary>
        /// Build the OData Atom primary keystring representation
        /// </summary>
        /// <param name="live">Entity for which primary key is required</param>
        /// <returns>String representation of the primary key</returns>
        public static string GetPrimaryKeyString(IOfflineEntity live)
        {
            StringBuilder builder = new StringBuilder();

            string sep = string.Empty;

            foreach (PropertyInfo keyInfo in GetPrimaryKeysPropertyInfoMapping(live.GetType()))
            {
                if (keyInfo.PropertyType == FormatterConstants.GuidType)
                {
                    builder.AppendFormat("{0}{1}=guid'{2}'", sep, keyInfo.Name, keyInfo.GetValue(live, null));
                }
                else if (keyInfo.PropertyType == FormatterConstants.StringType)
                {
                    builder.AppendFormat("{0}{1}='{2}'", sep, keyInfo.Name, keyInfo.GetValue(live, null));
                }
                else
                {
                    builder.AppendFormat("{0}{1}={2}", sep, keyInfo.Name, keyInfo.GetValue(live, null));
                }

                if (string.IsNullOrEmpty(sep))
                {
                    sep = ", ";
                }
            }
            return(builder.ToString());
        }
Пример #5
0
        /// <summary>
        /// Writes the <entry/> tag and all its related elements.
        /// </summary>
        /// <param name="live">Actual entity whose value is to be emitted.</param>
        /// <param name="tempId">The temporary Id if any</param>
        /// <param name="emitPartial">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        /// <returns>XElement representation of the entry element</returns>
        private XElement WriteEntry(IOfflineEntity live, string tempId, bool emitPartial)
        {
            string typeName = live.GetType().FullName;

            if (!live.ServiceMetadata.IsTombstone)
            {
                var entryElement = new XElement(FormatterConstants.AtomXmlNamespace + C.Entry);
                entryElement.Add(new XAttribute(C.Caption, typeName));

                if (!emitPartial)
                {
                    // Write the entity contents
                    WriteEntityContents(entryElement, live);
                }

                return(entryElement);
            }
            // Write the at:deleted-entry tombstone element
            var  tombstoneElement = new XElement(FormatterConstants.AtomXmlNamespace + C.Tombstone, new XAttribute(C.Caption, typeName));
            Guid id;

            if (!Guid.TryParse(live.ServiceMetadata.Id, out id))
            {
                string[] split = live.ServiceMetadata.Id.Split('\'');
                id = Guid.Parse(split[1]);
            }
            tombstoneElement.Add(id.ToString());
            return(tombstoneElement);
        }
Пример #6
0
        /// <summary>
        /// Adds an IOfflineEntity and its associated Conflicting/Error entity as an Atom entry element
        /// </summary>
        /// <param name="live">Live Entity</param>
        /// <param name="liveTempId">TempId for the live entity</param>
        /// <param name="conflicting">Conflicting entity that will be sent in synnConflict or syncError extension</param>
        /// <param name="conflictingTempId">TempId for the conflicting entity</param>
        /// <param name="desc">Error description or the conflict resolution</param>
        /// <param name="isConflict">Denotes if its an errorElement or conflict. Used only when <paramref name="desc"/> is not null</param>
        /// <param name="emitMetadataOnly">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        public override void WriteItemInternal(IOfflineEntity live, string liveTempId, IOfflineEntity conflicting, string conflictingTempId, string desc, bool isConflict, bool emitMetadataOnly)
        {
            XElement entryElement = WriteEntry(live, null, liveTempId, emitMetadataOnly);

            if (conflicting != null)
            {
                XElement conflictElement = new XElement(((isConflict) ? FormatterConstants.JsonSyncConflictElementName : FormatterConstants.JsonSyncErrorElementName),
                                                        new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Object));

                // Write the confliction resolution or errorElement.
                conflictElement.Add(new XElement(((isConflict) ? FormatterConstants.ConflictResolutionElementName : FormatterConstants.ErrorDescriptionElementName),
                                                 new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                                                 desc));

                // Write the confliction resolution or errorElement.
                XElement conflictingEntryElement = new XElement(((isConflict) ? FormatterConstants.ConflictEntryElementName : FormatterConstants.ErrorEntryElementName),
                                                                new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Object));

                // Write the Conflicting entry in
                WriteEntry(conflicting, conflictingEntryElement, conflictingTempId, false /*emitPartial*/);

                // Add the conflicting entry data in to the conflict element
                conflictElement.Add(conflictingEntryElement);

                // Add the conflict element to the live entry
                entryElement.Add(conflictElement);
            }

            _results.Add(entryElement);
        }
Пример #7
0
        /// <summary>
        /// This writes the public contents of the Entity in the properties element.
        /// </summary>
        /// <param name="entity">Entity</param>
        /// <returns>XElement representation of the properties element</returns>
        XElement WriteEntityContents(IOfflineEntity entity)
        {
            XElement contentElement = new XElement(FormatterConstants.ODataMetadataNamespace + FormatterConstants.PropertiesElementName);

            // Write only the primary keys if its an tombstone
            PropertyInfo[] properties = ReflectionUtility.GetPropertyInfoMapping(entity.GetType());

            // Write individual properties to the feed,
            foreach (PropertyInfo fi in properties)
            {
                string edmType   = FormatterUtilities.GetEdmType(fi.PropertyType);
                object value     = fi.GetValue(entity, null);
                string valString = value as string;
                if (valString != null)
                {
                    value = AtomHelper.CleanInvalidXmlChars(valString);
                }
                Type propType = fi.PropertyType;
                if (fi.PropertyType.IsGenericType && fi.PropertyType.Name.Equals(FormatterConstants.NullableTypeName, StringComparison.InvariantCulture))
                {
                    // Its a Nullable<T> property
                    propType = fi.PropertyType.GetGenericArguments()[0];
                }

                if (value == null)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + fi.Name,
                                     new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                                     new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubIsNullElementName, true)));
                }
                else if (propType == FormatterConstants.DateTimeType ||
                         propType == FormatterConstants.TimeSpanType ||
                         propType == FormatterConstants.DateTimeOffsetType)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + fi.Name,
                                     new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                                     FormatterUtilities.ConvertDateTimeForType_Atom(value, propType)));
                }
                else if (propType != FormatterConstants.ByteArrayType)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + fi.Name,
                                     new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                                     value));
                }
                else
                {
                    byte[] bytes = (byte[])value;
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + fi.Name,
                                     new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                                     Convert.ToBase64String(bytes)));
                }
            }

            return(contentElement);
        }
        /// <summary>
        /// Used when rejecting an item during upload request. Rejection may be due to validation errors or other 
        /// business rules. The runtime will send an appropriate SyncError for this entity back to client.
        /// </summary>
        /// <param name="entity">The entity to reject</param>
        /// <param name="rejectDescription">Reason for rejecting the change</param>
        public void RejectChange(IOfflineEntity entity, string rejectDescription)
        {
            WebUtil.CheckArgumentNull(entity, "entity");
            WebUtil.CheckArgumentNull(rejectDescription, "rejectDescription");
            WebUtil.CheckArgumentEmpty(rejectDescription, "rejectDescription");

            this._rejectedEntities[entity] = rejectDescription;
        }
Пример #9
0
 internal void AddItem(IOfflineEntity iOfflineEntity)
 {
     if (Data == null)
     {
         Data = new List <IOfflineEntity>();
     }
     Data.Add(iOfflineEntity);
 }
        /// <summary>
        /// Used when rejecting an item during upload request. Rejection may be due to validation errors or other
        /// business rules. The runtime will send an appropriate SyncError for this entity back to client.
        /// </summary>
        /// <param name="entity">The entity to reject</param>
        /// <param name="rejectDescription">Reason for rejecting the change</param>
        public void RejectChange(IOfflineEntity entity, string rejectDescription)
        {
            WebUtil.CheckArgumentNull(entity, "entity");
            WebUtil.CheckArgumentNull(rejectDescription, "rejectDescription");
            WebUtil.CheckArgumentEmpty(rejectDescription, "rejectDescription");

            this._rejectedEntities[entity] = rejectDescription;
        }
Пример #11
0
 internal void AddItem(IOfflineEntity iOfflineEntity)
 {
     if (Data == null)
     {
         Data = new List<IOfflineEntity>();
     }
     Data.Add(iOfflineEntity);
 }
Пример #12
0
        public static IOfflineEntity GetObjectForType(EntryInfoWrapper wrapper, Type[] knownTypes)
        {
            Type entityType;

            ConstructorInfo ctorInfo;

            // See if its cached first.
            if (!_stringToCtorInfoMapping.TryGetValue(wrapper.TypeName, out ctorInfo))
            {
                // Its not cached. Try to look for it then in list of known types.
                if (knownTypes != null)
                {
                    entityType = knownTypes.FirstOrDefault(e => e.FullName.Equals(wrapper.TypeName, StringComparison.CurrentCultureIgnoreCase));

                    if (entityType == null)
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to find a matching type for entry '{0}' in list of KnownTypes.", wrapper.TypeName));
                    }
                }
                else
                {
                    throw new InvalidOperationException(string.Format("Unable to find a matching type for entry '{0}' in the loaded assemblies. Specify the type name in the KnownTypes argument to the SyncReader instance.", wrapper.TypeName));
                }

                // Reflect this entity and get necessary info
                GetPropertyInfoMapping(entityType);
                ctorInfo = _stringToCtorInfoMapping[wrapper.TypeName];
            }
            else
            {
                entityType = ctorInfo.DeclaringType;
            }

            // Invoke the ctor
            object obj = ctorInfo.Invoke(null);

            // Set the parameters only for non tombstone items
            if (!wrapper.IsTombstone)
            {
                IEnumerable <PropertyInfo> props = GetPropertyInfoMapping(entityType);
                foreach (PropertyInfo pinfo in props)
                {
                    string value;
                    if (wrapper.PropertyBag.TryGetValue(pinfo.Name, out value))
                    {
                        pinfo.SetValue(obj, GetValueFromType(pinfo.PropertyType, value), null);
                    }
                }
            }

            IOfflineEntity entity = (IOfflineEntity)obj;

            entity.SetServiceMetadata(new OfflineEntityMetadata(wrapper.IsTombstone, wrapper.Id, wrapper.ETag,
                                                                wrapper.EditUri));
            return(entity);
        }
Пример #13
0
        /// <summary>
        /// This is called for every conflict/error with the client entity.
        /// We use this function to check if the error was a client insert and if yes then remove it from
        /// the list of _incomingNewInsertEntities. This way we know which entities are successful uploads so
        /// we can pass this info to the UploadResponseInterceptor.OutgoingChanges list.
        /// </summary>
        /// <param name="entity">The client version of the entity. We match the actual reference via its primary key</param>
        internal void ClientChangeFailedToApply(IOfflineEntity entity)
        {
            string primaryKey = ReflectionUtility.GetPrimaryKeyString(entity);
            // Check to see if this element is in the insert list and if yes then remove it
            IOfflineEntity matchedEntity = this._incomingNewInsertEntities.Where(
                e => ReflectionUtility.GetPrimaryKeyString(e).Equals(primaryKey, StringComparison.InvariantCulture)).FirstOrDefault();

            if (matchedEntity != null)
            {
                this._incomingNewInsertEntities.Remove(matchedEntity);
            }
        }
Пример #14
0
        /// <summary>
        /// Generate the Id for an entity. The format currently is the OData Id.
        /// i.e. http://baseUri/tableName(primarykeylist)
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        internal static string GenerateOfflineEntityId(IOfflineEntity entity)
        {
            var primaryKeyString = ReflectionUtility.GetPrimaryKeyString(entity);

            if (String.IsNullOrEmpty(primaryKeyString))
            {
                throw SyncServiceException.CreateInternalServerError(
                          String.Format("GetPrimaryKeyString method returned an empty string for entity type {0}.", entity.GetType()));
            }

            return(String.Format(@"{0}/{1}({2})", "http://bitmobile.com/" + AppDomain.CurrentDomain.FriendlyName /*WebOperationContext.Current.IncomingRequest.UriTemplateMatch.BaseUri*/, entity.GetType().Name, primaryKeyString));
        }
Пример #15
0
        /// <summary>
        /// Copies the individual properties from the entity back in to the DataTable's first row.
        /// This should be used only when merging a user conflict resolution back in to a DataRow.
        /// This returns the merged results as an object array
        /// </summary>
        /// <param name="entity">Entity from which to read values</param>
        /// <param name="table">The Table whose first DataRow will be updated.</param>
        /// <returns>The contents of the DataRow as an object array</returns>
        internal object[] CopyEntityToDataRow(IOfflineEntity entity, DataTable table)
        {
            Debug.Assert(table.Rows.Count == 1, "table.Rows.Count ==1");
            if (table.Rows.Count != 1)
            {
                throw new InvalidOperationException("Cannot copy Entity to a DataTable whose row count != 1");
            }

            Dictionary <string, string> mappingInfo = _localToGlobalPropertyMapping[entity.GetType()];

            // Check for tombstones
            bool isRowDeleted = false;

            if (table.Rows[0].RowState == DataRowState.Deleted)
            {
                isRowDeleted = true;
                table.Rows[0].RejectChanges();
            }


            // Retrieve the current row values
            object[]       rowValues  = table.Rows[0].ItemArray;
            PropertyInfo[] properties = entity.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            for (int i = 0; i < table.Columns.Count; i++)
            {
                // Iterate over each non sync column and read its value from the entity
                if (IsSyncSpecificColumn(table.Columns[i].ColumnName))
                {
                    continue;
                }

                // Read the value of the property
                string columnName = null;
                mappingInfo.TryGetValue(table.Columns[i].ColumnName, out columnName);
                columnName = columnName ?? table.Columns[i].ColumnName;

                // Retrieve the PropertyInfo
                PropertyInfo info = properties.Where(e => e.Name.Equals(columnName, StringComparison.Ordinal)).FirstOrDefault();

                Debug.Assert(info != null, "PropertyInfo is not null.");
                // Get the property's value and put it in the object array
                rowValues[i] = info.GetValue(entity, null) ?? DBNull.Value;
            }

            // Write the row values back to the DataRow
            table.Rows[0].ItemArray = rowValues;

            if (isRowDeleted)
            {
                table.Rows[0].Delete();
            }
            return(rowValues);
        }
Пример #16
0
        /// <summary>
        /// Called to add a Sync conflict item
        /// </summary>
        /// <param name="winningEntry">the winning entity</param>
        /// <param name="winningEntryTempId">the winning entity's tempId</param>
        /// <param name="losingEntry">The losing entity</param>
        /// <param name="losingEntryTempId">The losing entity's tempId</param>
        /// <param name="resolution">The conflict resolution aplied by the server</param>
        public virtual void AddConflictItem(IOfflineEntity winningEntry, string winningEntryTempId, IOfflineEntity losingEntry, string losingEntryTempId, SyncConflictResolution resolution)
        {
            if (winningEntry == null)
            {
                throw new ArgumentNullException("winningEntry");
            }

            if (losingEntry == null)
            {
                throw new ArgumentNullException("losingEntry");
            }
            WriteItemInternal(winningEntry, winningEntryTempId, losingEntry /*conflicting*/, losingEntryTempId, resolution.ToString() /*desc*/, true /*isconflict*/, false /*emitMetadataOnly*/);
        }
        public SyncConflictResolution ConflictHandler(SyncConflictContext context, out IOfflineEntity mergedEntity)
        {
            // Add a dummy header to the response indicating that the interceptor method was invoked.
            context.ResponseHeaders.Add("ConflictInterceptorFired", "true");

            // The mergedEntity is used as the winner of the conflict
            // when this method returns SyncConflictResolution.Merge as the resolution.
            // For other resolutions such as SyncConflictResolution.ClientWins and SyncConflictResolution.ServerWins
            // the mergedEntity should be set to null.
            mergedEntity = null;

            return(SyncConflictResolution.ClientWins);
        }
Пример #18
0
        /// <summary>
        /// Called to add a Sync Error item
        /// </summary>
        /// <param name="liveEntry">Live version of the entity</param>
        /// <param name="errorEntry">Version of the entity that caused the error.</param>
        /// <param name="errorEntryTempId">TempIf for the entity that caused the error.</param>
        /// <param name="errorDescription">Description of error.</param>
        public virtual void AddErrorItem(IOfflineEntity liveEntry, IOfflineEntity errorEntry, string errorEntryTempId, string errorDescription)
        {
            if (liveEntry == null)
            {
                throw new ArgumentNullException("liveEntry");
            }

            if (errorEntry == null)
            {
                throw new ArgumentNullException("errorEntry");
            }
            WriteItemInternal(liveEntry, null /*liveEntryTempId*/, errorEntry /*conflicting*/, errorEntryTempId, errorDescription /*desc*/, false /*isconflict*/, false /*emitMetadataOnly*/);
        }
Пример #19
0
        /// <summary>
        /// Called to add a particular Entity
        /// </summary>
        /// <param name="entry">Entity to add to serialize to the stream</param>
        /// <param name="tempId">TempId for the Entity</param>
        /// <param name="emitMetadataOnly">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        public virtual void AddItem(IOfflineEntity entry, string tempId, bool emitMetadataOnly)
        {
            if (entry == null)
            {
                throw new ArgumentNullException("entry");
            }

            if(string.IsNullOrEmpty(entry.ServiceMetadata.Id) && entry.ServiceMetadata.IsTombstone)
            {
                // Skip sending tombstones that dont have an Id as these were local create + delete.
                return; 
            }
            WriteItemInternal(entry, tempId, null /*conflicting*/, null/*conflictingTempId*/, null /*desc*/, false /*isconflict*/, emitMetadataOnly);
        }
        /// <summary>
        /// Called to add a particular Entity
        /// </summary>
        /// <param name="entry">Entity to add to serialize to the stream</param>
        /// <param name="tempId">TempId for the Entity</param>
        /// <param name="emitMetadataOnly">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        public virtual void AddItem(IOfflineEntity entry, string tempId, bool emitMetadataOnly)
        {
            if (entry == null)
            {
                throw new ArgumentNullException("entry");
            }

            if (string.IsNullOrEmpty(entry.ServiceMetadata.Id) && entry.ServiceMetadata.IsTombstone)
            {
                // Skip sending tombstones that dont have an Id as these were local create + delete.
                return;
            }
            WriteItemInternal(entry, tempId, null /*conflicting*/, null /*conflictingTempId*/, null /*desc*/, false /*isconflict*/, emitMetadataOnly);
        }
Пример #21
0
        /// <summary>
        /// This function loops the rejected entites and sends back a SyncError for each entity. For each entity it does the following
        /// 1. Retrieve the current version in server.
        /// 1.a If its null then it copies the primary key to a new object and marks it as tombstone.
        /// 2. Adds the SyncError to existing list of SyncErrors.
        /// </summary>
        /// <param name="sqlProvider"></param>
        private void ProcessRejectedEntities(SqlSyncProviderService sqlProvider)
        {
            if (this._rejectedEntities == null || this._rejectedEntities.Count == 0)
            {
                return;
            }

            try
            {
                List <IOfflineEntity> serverVersions = sqlProvider.GetCurrentServerVersionForEntities(this._rejectedEntities.Keys);
                if (serverVersions.Count != this._rejectedEntities.Count)
                {
                    // Ensure we get a server version for each entity we passed
                    throw new InvalidOperationException("Did not get server versions for all rejected entities.");
                }
                for (int i = 0; i < this._rejectedEntities.Keys.Count; i++)
                {
                    IOfflineEntity server = serverVersions[i];
                    IOfflineEntity client = this._rejectedEntities.Keys.ElementAt(i);

                    if (server == null)
                    {
                        // This means the server didnt contain a version for the entity. Need to send a tombstone back then
                        // create a new object and copy the values over
                        ConstructorInfo constructorInfo = client.GetType().GetConstructor(Type.EmptyTypes);
                        server = (IOfflineEntity)constructorInfo.Invoke(null);
                        // Copy the primary key values over
                        foreach (PropertyInfo info in ReflectionUtility.GetPrimaryKeysPropertyInfoMapping(client.GetType()))
                        {
                            info.SetValue(server, info.GetValue(client, null), null);
                        }
                        server.ServiceMetadata.IsTombstone = true;
                    }

                    SyncError error = new SyncError()
                    {
                        ErrorEntity = client,
                        LiveEntity  = server,
                        Description = this._rejectedEntities[client]
                    };

                    _applyChangesResponse.Errors.Add(error);
                }
            }
            catch (Exception e)
            {
                throw new InvalidOperationException("Error in reading server row values. " + e.Message);
            }
        }
Пример #22
0
        /// <summary>
        /// Adds an IOfflineEntity and its associated Conflicting/Error entity as an Atom entry element
        /// </summary>
        /// <param name="live">Live Entity</param>
        /// <param name="liveTempId">TempId for the live entity</param>
        /// <param name="conflicting">Conflicting entity that will be sent in synnConflict or syncError extension</param>
        /// <param name="conflictingTempId">TempId for the conflicting entity</param>
        /// <param name="desc">Error description or the conflict resolution</param>
        /// <param name="isConflict">Denotes if its an errorElement or conflict. Used only when <paramref name="desc"/> is not null</param>
        /// <param name="emitMetadataOnly">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        public override void WriteItemInternal(IOfflineEntity live, string liveTempId, IOfflineEntity conflicting, string conflictingTempId, string desc, bool isConflict, bool emitMetadataOnly)
        {
            XElement entryElement = WriteEntry(live, liveTempId, emitMetadataOnly);

            if (conflicting != null)
            {
                XElement conflictElement = new XElement(FormatterConstants.SyncNamespace + ((isConflict) ? FormatterConstants.SyncConlflictElementName : FormatterConstants.SyncErrorElementName));

                // Write the confliction resolution or errorElement.
                conflictElement.Add(new XElement(FormatterConstants.SyncNamespace + ((isConflict) ? FormatterConstants.ConflictResolutionElementName : FormatterConstants.ErrorDescriptionElementName), desc));

                // Write the confliction resolution or errorElement.

                XElement conflictingEntryElement = new XElement(FormatterConstants.SyncNamespace + ((isConflict) ? FormatterConstants.ConflictEntryElementName : FormatterConstants.ErrorEntryElementName));

                conflictingEntryElement.Add(WriteEntry(conflicting, conflictingTempId, false /*emitPartial*/));

                conflictElement.Add(conflictingEntryElement);

                entryElement.Add(conflictElement);
            }
            _root.Add(entryElement);
        }
Пример #23
0
        /// <summary>
        /// This writes the public contents of the Entity in the properties element.
        /// </summary>
        /// <param name="element"></param>
        /// <param name="entity">Entity</param>
        /// <returns>XElement representation of the properties element</returns>
        void WriteEntityContents(XElement element, IOfflineEntity entity)
        {
            // Write only the primary keys if its an tombstone
            PropertyInfo[] properties = ReflectionUtility.GetPropertyInfoMapping(entity.GetType());

            // Write individual properties to the feed,
            foreach (PropertyInfo fi in properties.OrderBy(val => val.Name.StartsWith("__") ? val.Name.Substring(2) : val.Name))
            {
                object value    = fi.GetValue(entity, null);
                Type   propType = fi.PropertyType;
                if (fi.PropertyType.IsGenericType && fi.PropertyType.Name.Equals(FormatterConstants.NullableTypeName, StringComparison.InvariantCulture))
                {
                    // Its a Nullable<T> property
                    propType = fi.PropertyType.GetGenericArguments()[0];
                }

                if (value == null)
                {
                    element.Add(new XElement(FormatterConstants.AtomXmlNamespace + C.NullableProperty));
                }
                else if (propType == FormatterConstants.DateTimeType ||
                         propType == FormatterConstants.TimeSpanType ||
                         propType == FormatterConstants.DateTimeOffsetType)
                {
                    element.Add(new XElement(FormatterConstants.AtomXmlNamespace + C.Property, FormatterUtilities.ConvertDateTimeForType_Atom(value, propType)));
                }
                else if (propType != FormatterConstants.ByteArrayType)
                {
                    element.Add(new XElement(FormatterConstants.AtomXmlNamespace + C.Property, value));
                }
                else
                {
                    byte[] bytes = (byte[])value;
                    element.Add(new XElement(FormatterConstants.AtomXmlNamespace + C.Property, Convert.ToBase64String(bytes)));
                }
            }
        }
Пример #24
0
        /// <summary>
        /// This writes the public contents of the Entity in the properties element.
        /// </summary>
        /// <param name="entity">Entity</param>
        /// <returns>XElement representation of the properties element</returns>
        XElement WriteEntityContents(IOfflineEntity entity)
        {
            XElement contentElement = new XElement(FormatterConstants.ODataMetadataNamespace + FormatterConstants.PropertiesElementName);

            // Write only the primary keys if its an tombstone
            var        ent  = (IEntity)entity;
            EntityType type = ent.EntityType;

            // Write individual properties to the feed,
            foreach (EntityField field in type.Fields)
            {
                Type   propType = field.Type;
                string name     = field.Name;
                object value    = ent.GetValue(field.Name);
                if (propType == typeof(IDbRef))
                {
                    propType = typeof(Guid);
                    var dbRef = (IDbRef)value;
                    value = dbRef.Id;
                    name  = "__" + name;
                }

                string edmType = FormatterUtilities.GetEdmType(propType);
                if (edmType == null)
                {
                    continue;
                }


                if (propType.IsGenericType && propType.Name.Equals(FormatterConstants.NullableTypeName, StringComparison.InvariantCulture))
                {
                    // Its a Nullable<T> property
                    propType = propType.GetGenericArguments()[0];
                }

                if (value == null)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + name,
                                     new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                                     new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubIsNullElementName, true)));
                }
                else if (propType == FormatterConstants.DateTimeType ||
                         propType == FormatterConstants.TimeSpanType ||
                         propType == FormatterConstants.DateTimeOffsetType)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + name,
                                     new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                                     FormatterUtilities.ConvertDateTimeForType_Atom(value, propType)));
                }
                else if (propType != FormatterConstants.ByteArrayType)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + name,
                                     new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                                     value));
                }
                else
                {
                    byte[] bytes = (byte[])value;
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + name,
                                     new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                                     Convert.ToBase64String(bytes)));
                }
            }

            return(contentElement);
        }
        /// <summary>
        /// Adds an IOfflineEntity and its associated Conflicting/Error entity as an Atom entry element
        /// </summary>
        /// <param name="live">Live Entity</param>
        /// <param name="liveTempId">TempId for the live entity</param>
        /// <param name="conflicting">Conflicting entity that will be sent in synnConflict or syncError extension</param>
        /// <param name="conflictingTempId">TempId for the conflicting entity</param>
        /// <param name="desc">Error description or the conflict resolution</param>
        /// <param name="isConflict">Denotes if its an errorElement or conflict. Used only when <paramref name="desc"/> is not null</param>
        /// <param name="emitMetadataOnly">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        public override void WriteItemInternal(IOfflineEntity live, string liveTempId, IOfflineEntity conflicting, string conflictingTempId, string desc, bool isConflict, bool emitMetadataOnly)
        {
            XElement entryElement = WriteEntry(live, null, liveTempId, emitMetadataOnly);

            if (conflicting != null)
            {
                XElement conflictElement = new XElement(((isConflict) ? FormatterConstants.JsonSyncConflictElementName : FormatterConstants.JsonSyncErrorElementName),
                    new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Object));

                // Write the confliction resolution or errorElement.
                conflictElement.Add(new XElement(((isConflict) ? FormatterConstants.ConflictResolutionElementName : FormatterConstants.ErrorDescriptionElementName),
                    new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                    desc));

                // Write the confliction resolution or errorElement.
                XElement conflictingEntryElement = new XElement(((isConflict) ? FormatterConstants.ConflictEntryElementName : FormatterConstants.ErrorEntryElementName),
                                        new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Object));

                // Write the Conflicting entry in
                WriteEntry(conflicting, conflictingEntryElement, conflictingTempId, false/*emitPartial*/);

                // Add the conflicting entry data in to the conflict element
                conflictElement.Add(conflictingEntryElement);

                // Add the conflict element to the live entry
                entryElement.Add(conflictElement);
            }

            _results.Add(entryElement);
        }
Пример #26
0
 public void AddUpdatedItem(IOfflineEntity item)
 {
     this._updatedItems.Add(item);
 }
Пример #27
0
        /// <summary>
        /// This writes the public contents of the Entity in the properties element.
        /// </summary>
        /// <param name="entity">Entity</param>
        /// <returns>XElement representation of the properties element</returns>
        XElement WriteEntityContents(IOfflineEntity entity)
        {
            XElement contentElement = new XElement(FormatterConstants.ODataMetadataNamespace + FormatterConstants.PropertiesElementName);

            // Write only the primary keys if its an tombstone
            IEnumerable<PropertyInfo> properties = ReflectionUtility.GetPropertyInfoMapping(entity.GetType());

            // Write individual properties to the feed,
            foreach (PropertyInfo fi in properties)
            {
                string edmType = FormatterUtilities.GetEdmType(fi.PropertyType);
                object value = fi.GetValue(entity, null);
                Type propType = fi.PropertyType;
                if(fi.PropertyType.IsGenericType() && fi.PropertyType.Name.Equals(FormatterConstants.NullableTypeName, StringComparison.CurrentCultureIgnoreCase))
                {
                    // Its a Nullable<T> property
                    propType = fi.PropertyType.GetGenericArguments()[0];
                }

                if (value == null)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + fi.Name,
                            new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                            new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubIsNullElementName, true)));
                }
                else if (propType == FormatterConstants.DateTimeType ||
                    propType == FormatterConstants.TimeSpanType ||
                    propType == FormatterConstants.DateTimeOffsetType)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + fi.Name,
                            new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                            FormatterUtilities.ConvertDateTimeForType_Atom(value, propType)));

                }
                else if (propType != FormatterConstants.ByteArrayType)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + fi.Name,
                            new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                            value));
                }
                else
                {
                    byte[] bytes = (byte[])value;
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + fi.Name,
                            new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                            Convert.ToBase64String(bytes)));
                }
            }

            return contentElement;
        }
Пример #28
0
 public abstract void WriteItemInternal(IOfflineEntity live, string liveTempId, IOfflineEntity conflicting, string conflictingTempId, string desc, bool isConflict, bool emitMetadataOnly);
        /// <summary>
        /// Check Metadata
        /// </summary>
        private void CheckEntityServiceMetadataAndTempIds(AsyncArgsWrapper wrapper, IOfflineEntity entity, string tempId)
        {
            // Check service ID 
            if (string.IsNullOrEmpty(entity.GetServiceMetadata().Id))
                throw new CacheControllerException(
                    string.Format("Service did not return a permanent Id for tempId '{0}'", tempId));

            // If an entity has a temp id then it should not be a tombstone                
            if (entity.GetServiceMetadata().IsTombstone)
                throw new CacheControllerException(string.Format(
                    "Service returned a tempId '{0}' in tombstoned entity.", tempId));

            // Check that the tempId was sent by client
            if (!wrapper.TempIdToEntityMapping.ContainsKey(tempId))
                throw new CacheControllerException(
                    "Service returned a response for a tempId which was not uploaded by the client. TempId: " + tempId);

            // Once received, remove the tempId from the mapping list.
            wrapper.TempIdToEntityMapping.Remove(tempId);
        }
Пример #30
0
        /// <summary>
        /// Writes the <entry/> tag and all its related elements.
        /// </summary>
        /// <param name="live">Actual entity whose value is to be emitted.</param>
        /// <param name="tempId">The temporary Id if any</param>
        /// <param name="emitPartial">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        /// <returns>XElement representation of the entry element</returns>
        private XElement WriteEntry(IOfflineEntity live, string tempId, bool emitPartial)
        {
            var entity = (IEntity)live;
            string typeName = string.Format("{0}.{1}.{2}", RootNamespace, entity.EntityType.Schema, entity.EntityType.Name);

            if (!live.ServiceMetadata.IsTombstone)
            {
                XElement entryElement = new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubEntryElementName);

                // Add Etag
                if (!string.IsNullOrEmpty(live.ServiceMetadata.ETag))
                {
                    entryElement.Add(new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.EtagElementName, live.ServiceMetadata.ETag));
                }

                // Add TempId element
                if (!string.IsNullOrEmpty(tempId))
                {
                    entryElement.Add(new XElement(FormatterConstants.SyncNamespace + FormatterConstants.TempIdElementName, tempId));
                }

                // Add Id element
                entryElement.Add(new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubIdElementName,
                    string.IsNullOrEmpty(live.ServiceMetadata.Id) ? string.Empty : live.ServiceMetadata.Id));

                // Add title element
                entryElement.Add(new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubTitleElementName,
                        new XAttribute(FormatterConstants.AtomPubTypeElementName, "text")));

                // Add updated element
                entryElement.Add(new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubUpdatedElementName,
                            XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Utc)));

                // Add author element
                entryElement.Add(new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubAuthorElementName,
                            new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubNameElementName)));

                // Write the <link> element
                entryElement.Add(
                    new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubLinkElementName,
                        new XAttribute(FormatterConstants.AtomPubRelAttrName, FormatterConstants.AtomPubEditLinkAttributeName),
                        new XAttribute(FormatterConstants.AtomPubTitleElementName, typeName),
                        new XAttribute(FormatterConstants.AtomPubHrefAttrName,
                            (live.ServiceMetadata.EditUri != null) ? live.ServiceMetadata.EditUri.ToString() : string.Empty)));

                // Write the <category> element
                entryElement.Add(
                    new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubCategoryElementName,
                        new XAttribute(FormatterConstants.AtomPubTermAttrName, typeName),
                        new XAttribute(FormatterConstants.AtomPubSchemaAttrName, FormatterConstants.ODataSchemaNamespace)));

                XElement contentElement = new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubContentElementName);

                if (!emitPartial)
                {
                    // Write the entity contents
                    contentElement.Add(WriteEntityContents(live));
                }

                // Add the contents entity to the outer entity.
                entryElement.Add(contentElement);

                return entryElement;
            }
            else
            {
                // Write the at:deleted-entry tombstone element
                XElement tombstoneElement = new XElement(FormatterConstants.AtomDeletedEntryNamespace + FormatterConstants.AtomDeletedEntryElementName);
                tombstoneElement.Add(new XElement(FormatterConstants.AtomNamespaceUri + FormatterConstants.AtomReferenceElementName, live.ServiceMetadata.Id));
                tombstoneElement.Add(new XElement(FormatterConstants.SyncNamespace + FormatterConstants.AtomPubCategoryElementName, typeName));
                return tombstoneElement;
            }
        }
Пример #31
0
        /// <summary>
        /// This writes the public contents of the Entity in the properties element.
        /// </summary>
        /// <param name="element"></param>
        /// <param name="entity">Entity</param>
        /// <returns>XElement representation of the properties element</returns>
        void WriteEntityContents(XElement element, IOfflineEntity entity)
        {
            // Write only the primary keys if its an tombstone
            PropertyInfo[] properties = ReflectionUtility.GetPropertyInfoMapping(entity.GetType());

            // Write individual properties to the feed,
            foreach (PropertyInfo fi in properties.OrderBy(val => val.Name.StartsWith("__") ? val.Name.Substring(2) : val.Name))
            {
                object value = fi.GetValue(entity, null);
                Type propType = fi.PropertyType;
                if (fi.PropertyType.IsGenericType && fi.PropertyType.Name.Equals(FormatterConstants.NullableTypeName, StringComparison.InvariantCulture))
                {
                    // Its a Nullable<T> property
                    propType = fi.PropertyType.GetGenericArguments()[0];
                }

                if (value == null)
                {
                    element.Add(new XElement(FormatterConstants.AtomXmlNamespace + C.NullableProperty));
                }
                else if (propType == FormatterConstants.DateTimeType ||
                    propType == FormatterConstants.TimeSpanType ||
                    propType == FormatterConstants.DateTimeOffsetType)
                {
                    element.Add(new XElement(FormatterConstants.AtomXmlNamespace + C.Property, FormatterUtilities.ConvertDateTimeForType_Atom(value, propType)));
                }
                else if (propType != FormatterConstants.ByteArrayType)
                {
                    element.Add(new XElement(FormatterConstants.AtomXmlNamespace + C.Property, value));
                }
                else
                {
                    byte[] bytes = (byte[])value;
                    element.Add(new XElement(FormatterConstants.AtomXmlNamespace + C.Property, Convert.ToBase64String(bytes)));
                }
            }
        }
        /// <summary>
        /// Remove an entity from a conflicts collection. The conflicts collection contains all the 
        /// SyncConflict instances that are detected when applying changes to the server.
        /// </summary>
        /// <param name="tableName">Global table name of the entity</param>
        /// <param name="entity">Entity to remove from the conflicts collection</param>
        private void RemoveEntityFromConflictCollection(string tableName, IOfflineEntity entity)
        {
            SyncId errorEntitySyncId = GenerateSyncIdForConflictingEntity(tableName, entity);

            // Lookup the conflict entity in the dictionary that we record conflicts.
            SyncConflict conflict;
            _syncEntityIdToConflictMapping.TryGetValue(errorEntitySyncId, out conflict);

            // If a conflict was found...
            if (null != conflict)
            {
                _conflictToSyncEntityIdMapping.Remove(conflict);

                _syncEntityIdToConflictMapping.Remove(errorEntitySyncId);
                    
                // Remove the item from the conflicts collection.
                _conflicts.Remove(conflict);
            }
        }
Пример #33
0
        private void CheckEntityServiceMetadataAndTempIds(Dictionary<string, IOfflineEntity> tempIdToEntityMapping, 
            IOfflineEntity entity, string tempId, ChangeSetResponse response)
        {
            // Check service ID
            if (string.IsNullOrEmpty(entity.ServiceMetadata.Id))
            {
                throw new CacheControllerException(string.Format("Service did not return a permanent Id for tempId '{0}'", tempId));
            }

            // If an entity has a temp id then it should not be a tombstone
            if (entity.ServiceMetadata.IsTombstone)
            {
                throw new CacheControllerException(string.Format("Service returned a tempId '{0}' in tombstoned entity.", tempId));
            }

            // Check that the tempId was sent by client
            if (!tempIdToEntityMapping.ContainsKey(tempId))
            {
                throw new CacheControllerException("Service returned a response for a tempId which was not uploaded by the client. TempId: " + tempId);
            }

            // Add the entity to the Updated list.
            response.AddUpdatedItem(entity);

            // Once received, remove the tempId from the mapping list.
            tempIdToEntityMapping.Remove(tempId);
        }
Пример #34
0
 /// <summary>
 /// Utility for invoking user code for conflict interceptors
 /// </summary>
 /// <param name="context">The context to pass as parameter to user code</param>
 /// <param name="mergedVersion">The merged version for Merge resolution</param>
 /// <param name="entityType">Entity type of the conflict being raised</param>
 /// <returns>Actual resolution picked by user</returns>
 internal SyncConflictResolution? InvokeConflictInterceptor(SyncConflictContext context, Type entityType, out IOfflineEntity mergedVersion)
 {
     SyncInterceptorsInfoWrapper wrapper = null;
     if (this.SyncInterceptors.TryGetValue(context.ScopeName, out wrapper))
     {
         // Look for unfiltered Conflict and if that is null then look for filtered one.
         // Its an error to have both unfiltered and filtered ConflictInterceptor so both cannot be set.
         MethodInfo methodInfo = wrapper.ConflictInterceptor ?? wrapper.GetConflictInterceptor(entityType);
         if (methodInfo != null)
         {
             object[] inputParams = new object[] { context, null };
             SyncConflictResolution resolution = (SyncConflictResolution)InvokeUserInterceptorMethod(methodInfo, OperationContext.Current.InstanceContext.GetServiceInstance(), inputParams);
             // Merged version is in the second parameter which is passed by reference. Look it up
             mergedVersion = (IOfflineEntity)inputParams[1];
             return resolution;
         }
     }
     mergedVersion = null;
     return null;
 }
Пример #35
0
        /// <summary>
        /// Writes the Json object tag and all its related elements.
        /// </summary>
        /// <param name="live">Actual entity whose value is to be emitted.</param>
        /// <param name="entryElement">This is the parent entry element that is needs to go in to. Will be null for regular items and non null for
        /// conflict/error items only</param>
        /// <param name="tempId">The tempId for the element if passed in by the client.</param>
        /// <param name="emitPartial">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        /// <returns>XElement representation of the entry element</returns>
        private XElement WriteEntry(IOfflineEntity live, XElement entryElement, string tempId, bool emitPartial)
        {
            string typeName = live.GetType().FullName;

            if (entryElement == null)
            {
                entryElement = new XElement("item",
                                            new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Object));
            }

            // Write the _metadata object for this entry
            XElement entryMetadata = new XElement(FormatterConstants.JsonSyncEntryMetadataElementName,
                                                  new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Object));

            // Add the tempId to metadata
            if (!string.IsNullOrEmpty(tempId))
            {
                entryMetadata.Add(new XElement(FormatterConstants.TempIdElementName,
                                               new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String), tempId));
            }

            // Add the uri to metadata
            entryMetadata.Add(new XElement(FormatterConstants.JsonSyncEntryUriElementName,
                                           new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                                           string.IsNullOrEmpty(live.ServiceMetadata.Id) ? string.Empty : live.ServiceMetadata.Id));

            // Add the etag to metadata
            if (!string.IsNullOrEmpty(live.ServiceMetadata.ETag))
            {
                entryMetadata.Add(new XElement(FormatterConstants.EtagElementName,
                                               new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String), live.ServiceMetadata.ETag));
            }

            // Add the edituri to metadata
            if (live.ServiceMetadata.EditUri != null)
            {
                entryMetadata.Add(new XElement(FormatterConstants.EditUriElementName,
                                               new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String), live.ServiceMetadata.EditUri));
            }

            // Add the type to metadata
            entryMetadata.Add(new XElement(FormatterConstants.JsonSyncEntryTypeElementName,
                                           new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                                           typeName));

            // Write the tombstone element
            if (live.ServiceMetadata.IsTombstone)
            {
                entryMetadata.Add(new XElement(FormatterConstants.IsDeletedElementName,
                                               new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Boolean),
                                               true));
            }
            else if (!emitPartial)
            {
                // Write the entity contents only when its not a tombstone
                WriteEntityContentsToElement(entryElement, live);
            }

            // Add the metadata to the entry element
            entryElement.Add(entryMetadata);

            return(entryElement);
        }
Пример #36
0
        /// <summary>
        /// Callback for the Upload HttpWebRequest.BeginGetResponse call
        /// </summary>
        /// <param name="asyncResult">IAsyncResult object</param>
        void OnUploadGetResponseCompleted(IAsyncResult asyncResult)
        {
            AsyncArgsWrapper wrapper = asyncResult.AsyncState as AsyncArgsWrapper;

            wrapper.UploadResponse = new ChangeSetResponse();

            HttpWebResponse response = null;

            try
            {
                try
                {
                    response = wrapper.WebRequest.EndGetResponse(asyncResult) as HttpWebResponse;
                }
                catch (WebException we)
                {
                    wrapper.UploadResponse.Error = we;
                    // If we get here then it means we completed the request. Return to the original caller
                    this._workerManager.CompleteWorkRequest(wrapper.WorkerRequest, wrapper);
                    return;
                }
                catch (SecurityException se)
                {
                    wrapper.UploadResponse.Error = se;
                    // If we get here then it means we completed the request. Return to the original caller
                    this._workerManager.CompleteWorkRequest(wrapper.WorkerRequest, wrapper);
                    return;
                }

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    Stream responseStream = response.GetResponseStream();

                    // CreateInstance the SyncReader
                    if (ApplicationContext.Current.Settings.BitMobileFormatterDisabled)
                    {
                        _syncReader = new ODataAtomReader(responseStream, _knownTypes);
                    }
                    else
                    {
                        _syncReader = new BMReader(responseStream, _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(wrapper, entity, tempId);
                            }

                            //  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(wrapper, conflictEntity, tempId);
                                }

                                // Add conflict
                                wrapper.UploadResponse.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))
                            {
                                wrapper.UploadResponse.AddUpdatedItem(ackedEntity);
                            }
                            break;

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

                    wrapper.WebResponse = response;
                    // Invoke user code on the correct synchronization context.
                    this.FirePostResponseHandler(wrapper);
                }
                else
                {
                    wrapper.UploadResponse.Error = new CacheControllerException(
                        string.Format("Remote service returned error status. Status: {0}, Description: {1}",
                                      response.StatusCode,
                                      response.StatusDescription));
                }

                // If we get here then it means we completed the request. Return to the original caller
                this._workerManager.CompleteWorkRequest(wrapper.WorkerRequest, wrapper);
            }
            catch (Exception e)
            {
                if (ExceptionUtility.IsFatal(e))
                {
                    throw;
                }

                wrapper.Error = e;
                this._workerManager.CompleteWorkRequest(wrapper.WorkerRequest, wrapper);
            }
        }
        /// <summary>
        /// Writes the Json object tag and all its related elements.
        /// </summary>
        /// <param name="live">Actual entity whose value is to be emitted.</param>
        /// <param name="entryElement">This is the parent entry element that is needs to go in to. Will be null for regular items and non null for 
        /// conflict/error items only</param>
        /// <param name="tempId">The tempId for the element if passed in by the client.</param>
        /// <param name="emitPartial">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        /// <returns>XElement representation of the entry element</returns>
        private XElement WriteEntry(IOfflineEntity live, XElement entryElement, string tempId, bool emitPartial)
        {
            string typeName = live.GetType().FullName;

            if (entryElement == null)
            {
                entryElement = new XElement("item",
                   new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Object));
            }

            // Write the _metadata object for this entry
            XElement entryMetadata = new XElement(FormatterConstants.JsonSyncEntryMetadataElementName,
                new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Object));

            // Add the tempId to metadata
            if (!string.IsNullOrEmpty(tempId))
            {
                entryMetadata.Add(new XElement(FormatterConstants.TempIdElementName,
                    new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String), tempId));
            }

            // Add the uri to metadata
            entryMetadata.Add(new XElement(FormatterConstants.JsonSyncEntryUriElementName,
                new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                string.IsNullOrEmpty(live.ServiceMetadata.Id) ? string.Empty : live.ServiceMetadata.Id));

            // Add the etag to metadata
            if (!string.IsNullOrEmpty(live.ServiceMetadata.ETag))
            {
                entryMetadata.Add(new XElement(FormatterConstants.EtagElementName,
                    new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String), live.ServiceMetadata.ETag));
            }

            // Add the edituri to metadata
            if (live.ServiceMetadata.EditUri != null)
            {
                entryMetadata.Add(new XElement(FormatterConstants.EditUriElementName,
                    new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String), live.ServiceMetadata.EditUri));
            }

            // Add the type to metadata
            entryMetadata.Add(new XElement(FormatterConstants.JsonSyncEntryTypeElementName,
                new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                typeName));

            // Write the tombstone element
            if (live.ServiceMetadata.IsTombstone)
            {
                entryMetadata.Add(new XElement(FormatterConstants.IsDeletedElementName,
                    new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Boolean),
                    true));
            }
            else if (!emitPartial)
            {
                // Write the entity contents only when its not a tombstone
                WriteEntityContentsToElement(entryElement, live);
            }

            // Add the metadata to the entry element
            entryElement.Add(entryMetadata);

            return entryElement;
        }
Пример #38
0
        /// <summary>
        /// Adds an IOfflineEntity and its associated Conflicting/Error entity as an Atom entry element
        /// </summary>
        /// <param name="live">Live Entity</param>
        /// <param name="liveTempId">TempId for the live entity</param>
        /// <param name="conflicting">Conflicting entity that will be sent in synnConflict or syncError extension</param>
        /// <param name="conflictingTempId">TempId for the conflicting entity</param>
        /// <param name="desc">Error description or the conflict resolution</param>
        /// <param name="isConflict">Denotes if its an errorElement or conflict. Used only when <paramref name="desc"/> is not null</param>
        /// <param name="emitMetadataOnly">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        public override void WriteItemInternal(IOfflineEntity live, string liveTempId, IOfflineEntity conflicting, string conflictingTempId, string desc, bool isConflict, bool emitMetadataOnly)
        {
            XElement entryElement = WriteEntry(live, liveTempId, emitMetadataOnly);

            if (conflicting != null)
            {
                XElement conflictElement = new XElement(FormatterConstants.SyncNamespace + ((isConflict) ? FormatterConstants.SyncConlflictElementName : FormatterConstants.SyncErrorElementName));

                // Write the confliction resolution or errorElement.
                conflictElement.Add(new XElement(FormatterConstants.SyncNamespace + ((isConflict) ? FormatterConstants.ConflictResolutionElementName : FormatterConstants.ErrorDescriptionElementName), desc));

                // Write the confliction resolution or errorElement.

                XElement conflictingEntryElement = new XElement(FormatterConstants.SyncNamespace + ((isConflict) ? FormatterConstants.ConflictEntryElementName : FormatterConstants.ErrorEntryElementName));

                conflictingEntryElement.Add(WriteEntry(conflicting, conflictingTempId, false/*emitPartial*/));

                conflictElement.Add(conflictingEntryElement);

                entryElement.Add(conflictElement);
            }
            _root.Add(entryElement);
        }
Пример #39
0
        /// <summary>
        /// Called to add a Sync Error item
        /// </summary>
        /// <param name="liveEntry">Live version of the entity</param>
        /// <param name="errorEntry">Version of the entity that caused the error.</param>
        /// <param name="errorEntryTempId">TempIf for the entity that caused the error.</param>
        /// <param name="errorDescription">Description of error.</param>
        public virtual void AddErrorItem(IOfflineEntity liveEntry, IOfflineEntity errorEntry, string errorEntryTempId, string errorDescription)
        {
            if (liveEntry == null)
            {
                throw new ArgumentNullException("liveEntry");
            }

            if (errorEntry == null)
            {
                throw new ArgumentNullException("errorEntry");
            }
            WriteItemInternal(liveEntry, null/*liveEntryTempId*/, errorEntry/*conflicting*/, errorEntryTempId, errorDescription/*desc*/, false /*isconflict*/, false/*emitMetadataOnly*/);
        }
Пример #40
0
        /// <summary>
        /// This writes the public contents of the Entity in the properties element.
        /// </summary>
        /// <param name="entity">Entity</param>
        /// <returns>XElement representation of the properties element</returns>
        XElement WriteEntityContents(IOfflineEntity entity)
        {
            XElement contentElement = new XElement(FormatterConstants.ODataMetadataNamespace + FormatterConstants.PropertiesElementName);

            // Write only the primary keys if its an tombstone
            var ent = (IEntity)entity;
            IEntityType type = ent.EntityType;

            // Write individual properties to the feed,
            foreach (EntityField field in type.Fields)
            {
                Type propType = field.Type;
                string name = field.Name;
                object value = ent.GetValue(field.Name);
                if (propType == typeof(IDbRef))
                {
                    propType = typeof (Guid);
                    var dbRef = (IDbRef) value;
                    value = dbRef.Id;
                    name = "__" + name;
                }

                string edmType = FormatterUtilities.GetEdmType(propType);
                if (edmType == null)
                    continue;

                
                if (propType.IsGenericType && propType.Name.Equals(FormatterConstants.NullableTypeName, StringComparison.InvariantCulture))
                {
                    // Its a Nullable<T> property
                    propType = propType.GetGenericArguments()[0];
                }

                if (value == null)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + name,
                            new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                            new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubIsNullElementName, true)));
                }
                else if (propType == FormatterConstants.DateTimeType ||
                    propType == FormatterConstants.TimeSpanType ||
                    propType == FormatterConstants.DateTimeOffsetType)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + name,
                            new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                            FormatterUtilities.ConvertDateTimeForType_Atom(value, propType)));

                }
                else if (propType != FormatterConstants.ByteArrayType)
                {
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + name,
                            new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                            value));
                }
                else
                {
                    byte[] bytes = (byte[])value;
                    contentElement.Add(
                        new XElement(FormatterConstants.ODataDataNamespace + name,
                            new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.AtomPubTypeElementName, edmType),
                            Convert.ToBase64String(bytes)));
                }
            }

            return contentElement;
        }
Пример #41
0
		public void AddUpdatedItem(IOfflineEntity item)
        {
            this._updatedItems.Add(item);
        }
Пример #42
0
 /// <summary>
 /// Called to add a particular Entity
 /// </summary>
 /// <param name="entry">Entity to add to serialize to the stream</param>
 /// <param name="tempId">TempId for the Entity</param>
 public virtual void AddItem(IOfflineEntity entry, string tempId)
 {
     this.AddItem(entry, tempId, false);
 }
 /// <summary>
 /// This is called for every conflict/error with the client entity. 
 /// We use this function to check if the error was a client insert and if yes then remove it from
 /// the list of _incomingNewInsertEntities. This way we know which entities are successful uploads so
 /// we can pass this info to the UploadResponseInterceptor.OutgoingChanges list.
 /// </summary>
 /// <param name="entity">The client version of the entity. We match the actual reference via its primary key</param>
 internal void ClientChangeFailedToApply(IOfflineEntity entity)
 {
     string primaryKey = ReflectionUtility.GetPrimaryKeyString(entity);
     // Check to see if this element is in the insert list and if yes then remove it
     IOfflineEntity matchedEntity = this._incomingNewInsertEntities.Where(
         e => ReflectionUtility.GetPrimaryKeyString(e).Equals(primaryKey, StringComparison.InvariantCulture)).FirstOrDefault();
     if (matchedEntity != null)
     {
         this._incomingNewInsertEntities.Remove(matchedEntity);
     }
 }
        /// <summary>
        /// Utility for invoking user code for conflict interceptors
        /// </summary>
        /// <param name="context">The context to pass as parameter to user code</param>
        /// <param name="mergedVersion">The merged version for Merge resolution</param>
        /// <param name="entityType">Entity type of the conflict being raised</param>
        /// <returns>Actual resolution picked by user</returns>
        internal SyncConflictResolution?InvokeConflictInterceptor(SyncConflictContext context, Type entityType, out IOfflineEntity mergedVersion)
        {
            SyncInterceptorsInfoWrapper wrapper = null;

            if (this.SyncInterceptors.TryGetValue(context.ScopeName, out wrapper))
            {
                // Look for unfiltered Conflict and if that is null then look for filtered one.
                // Its an error to have both unfiltered and filtered ConflictInterceptor so both cannot be set.
                MethodInfo methodInfo = wrapper.ConflictInterceptor ?? wrapper.GetConflictInterceptor(entityType);
                if (methodInfo != null)
                {
                    object[] inputParams = new object[] { context, null };
                    SyncConflictResolution resolution = (SyncConflictResolution)InvokeUserInterceptorMethod(methodInfo, OperationContext.Current.InstanceContext.GetServiceInstance(), inputParams);
                    // Merged version is in the second parameter which is passed by reference. Look it up
                    mergedVersion = (IOfflineEntity)inputParams[1];
                    return(resolution);
                }
            }
            mergedVersion = null;
            return(null);
        }
        /// <summary>
        /// This writes the public contents of the Entity to the passed in XElement.
        /// </summary>
        /// <param name="contentElement">The XElement to which the type properties is added to</param>
        /// <param name="entity">Entity</param>
        /// <returns>XElement representation of the properties element</returns>
        void WriteEntityContentsToElement(XElement contentElement, IOfflineEntity entity)
        {
            PropertyInfo[] properties = ReflectionUtility.GetPropertyInfoMapping(entity.GetType());

            // Write individual properties to the feed,
            foreach (PropertyInfo fi in properties)
            {
                object objValue = fi.GetValue(entity, null);
                if (objValue == null)
                {
                    contentElement.Add(
                        new XElement(fi.Name,
                            new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Null),
                            objValue));
                }
                else if (fi.PropertyType == FormatterConstants.CharType ||
                    fi.PropertyType == FormatterConstants.StringType ||
                    fi.PropertyType == FormatterConstants.GuidType)
                {
                    contentElement.Add(
                        new XElement(fi.Name,
                            new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                            objValue));
                }
                else if (fi.PropertyType == FormatterConstants.DateTimeType ||
                    fi.PropertyType == FormatterConstants.TimeSpanType ||
                    fi.PropertyType == FormatterConstants.DateTimeOffsetType)
                {
                    contentElement.Add(
                        new XElement(fi.Name,
                            new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                            FormatterUtilities.ConvertDateTimeForType_Json(objValue, fi.PropertyType)));
                }
                else if (fi.PropertyType == FormatterConstants.BoolType)
                {
                    contentElement.Add(
                        new XElement(fi.Name,
                            new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Boolean),
                            objValue));
                }
                else if (fi.PropertyType == FormatterConstants.ByteArrayType)
                {
                    byte[] bytes = (byte[])objValue;
                    contentElement.Add(
                        new XElement(fi.Name,
                            new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                            Convert.ToBase64String(bytes)));
                }
                else if (fi.PropertyType.IsGenericType && fi.PropertyType.Name.Equals(FormatterConstants.NullableTypeName, StringComparison.InvariantCulture))
                {
                    // Its a Nullable<T> property
                    Type genericParamType = fi.PropertyType.GetGenericArguments()[0];

                    string elementType = JsonElementTypes.Number;
                    if (genericParamType == FormatterConstants.BoolType)
                    {
                        elementType = JsonElementTypes.Boolean;
                    }
                    else if (genericParamType == FormatterConstants.DateTimeType ||
                        genericParamType == FormatterConstants.TimeSpanType ||
                        genericParamType == FormatterConstants.DateTimeOffsetType)
                    {
                        contentElement.Add(
                            new XElement(fi.Name,
                                new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                                FormatterUtilities.ConvertDateTimeForType_Json(objValue, genericParamType)));
                        continue;
                    }
                    else if (genericParamType == FormatterConstants.CharType ||
                        genericParamType == FormatterConstants.GuidType)
                    {
                        elementType = JsonElementTypes.String;
                    }

                    contentElement.Add(
                        new XElement(fi.Name,
                            new XAttribute(FormatterConstants.JsonTypeAttributeName, elementType),
                            objValue));

                }
                else // Its a number
                {
                    contentElement.Add(
                        new XElement(fi.Name,
                            new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Number),
                            objValue));
                }
            }
        }
Пример #46
0
        /// <summary>
        /// Called to add a Sync conflict item
        /// </summary>
        /// <param name="winningEntry">the winning entity</param>
        /// <param name="winningEntryTempId">the winning entity's tempId</param>
        /// <param name="losingEntry">The losing entity</param>
        /// <param name="losingEntryTempId">The losing entity's tempId</param>
        /// <param name="resolution">The conflict resolution aplied by the server</param>
        public virtual void AddConflictItem(IOfflineEntity winningEntry, string winningEntryTempId, IOfflineEntity losingEntry, string losingEntryTempId, SyncConflictResolution resolution)
        {
            if (winningEntry == null)
            {
                throw new ArgumentNullException("winningEntry");
            }

            if (losingEntry == null)
            {
                throw new ArgumentNullException("losingEntry");
            }
            WriteItemInternal(winningEntry, winningEntryTempId, losingEntry/*conflicting*/, losingEntryTempId, resolution.ToString() /*desc*/, true/*isconflict*/, false/*emitMetadataOnly*/);
        }
 public abstract void WriteItemInternal(IOfflineEntity live, string liveTempId, IOfflineEntity conflicting, string conflictingTempId, string desc, bool isConflict, bool emitMetadataOnly);
        /// <summary>
        /// Get the entity from the incoming request that matches the primary key string of the entity passed as a parameter.
        /// </summary>
        /// <param name="primaryKeyToIncomingEntitiesMapping">Dictionary of mapping between primary key and the actual entities from incoming request.</param>
        /// <param name="entity">Entity for which to search a match in the incoming request.</param>
        /// <param name="isConflict">Indicates if this is called during conflict processing. Used to select appropriate error messages.</param>
        /// <returns>Entity from the incoming request.</returns>
        private static IOfflineEntity GetEntityFromIncomingRequest(Dictionary<string, IOfflineEntity> primaryKeyToIncomingEntitiesMapping,
                                                                   IOfflineEntity entity,
                                                                   bool isConflict)
        {
            // find the actual entity from the input list.
            var entityListInRequest = primaryKeyToIncomingEntitiesMapping.Where(e =>
                e.Key.Equals(ReflectionUtility.GetPrimaryKeyString(entity), StringComparison.InvariantCultureIgnoreCase)).ToList();

            // If no match is found, then throw an error.
            if (0 == entityListInRequest.Count)
            {
                if (isConflict)
                {
                    throw SyncServiceException.CreateInternalServerError(Strings.ConflictEntityMissingInIncomingRequest);
                }

                throw SyncServiceException.CreateInternalServerError(Strings.ErrorEntityMissingInIncomingRequest);
            }

            // If the entity corresponding to the key is null, then throw an error.
            if (null == entityListInRequest[0].Value)
            {
                if (isConflict)
                {
                    throw SyncServiceException.CreateInternalServerError(Strings.ConflictEntityMissingInIncomingRequest);
                }

                throw SyncServiceException.CreateInternalServerError(Strings.ErrorEntityMissingInIncomingRequest);
            }

            return entityListInRequest[0].Value;
        }
Пример #49
0
        /// <summary>
        /// Writes the <entry/> tag and all its related elements.
        /// </summary>
        /// <param name="live">Actual entity whose value is to be emitted.</param>
        /// <param name="tempId">The temporary Id if any</param>
        /// <param name="emitPartial">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        /// <returns>XElement representation of the entry element</returns>
        private XElement WriteEntry(IOfflineEntity live, string tempId, bool emitPartial)
        {
            var    entity   = (IEntity)live;
            string typeName = string.Format("{0}.{1}.{2}", RootNamespace, entity.EntityType.Schema, entity.EntityType.Name);

            if (!live.ServiceMetadata.IsTombstone)
            {
                XElement entryElement = new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubEntryElementName);

                // Add Etag
                if (!string.IsNullOrEmpty(live.ServiceMetadata.ETag))
                {
                    entryElement.Add(new XAttribute(FormatterConstants.ODataMetadataNamespace + FormatterConstants.EtagElementName, live.ServiceMetadata.ETag));
                }

                // Add TempId element
                if (!string.IsNullOrEmpty(tempId))
                {
                    entryElement.Add(new XElement(FormatterConstants.SyncNamespace + FormatterConstants.TempIdElementName, tempId));
                }

                // Add Id element
                entryElement.Add(new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubIdElementName,
                                              string.IsNullOrEmpty(live.ServiceMetadata.Id) ? string.Empty : live.ServiceMetadata.Id));

                // Add title element
                entryElement.Add(new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubTitleElementName,
                                              new XAttribute(FormatterConstants.AtomPubTypeElementName, "text")));

                // Add updated element
                entryElement.Add(new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubUpdatedElementName,
                                              XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Utc)));

                // Add author element
                entryElement.Add(new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubAuthorElementName,
                                              new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubNameElementName)));

                // Write the <link> element
                entryElement.Add(
                    new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubLinkElementName,
                                 new XAttribute(FormatterConstants.AtomPubRelAttrName, FormatterConstants.AtomPubEditLinkAttributeName),
                                 new XAttribute(FormatterConstants.AtomPubTitleElementName, typeName),
                                 new XAttribute(FormatterConstants.AtomPubHrefAttrName,
                                                (live.ServiceMetadata.EditUri != null) ? live.ServiceMetadata.EditUri.ToString() : string.Empty)));

                // Write the <category> element
                entryElement.Add(
                    new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubCategoryElementName,
                                 new XAttribute(FormatterConstants.AtomPubTermAttrName, typeName),
                                 new XAttribute(FormatterConstants.AtomPubSchemaAttrName, FormatterConstants.ODataSchemaNamespace)));

                XElement contentElement = new XElement(FormatterConstants.AtomXmlNamespace + FormatterConstants.AtomPubContentElementName);

                if (!emitPartial)
                {
                    // Write the entity contents
                    contentElement.Add(WriteEntityContents(live));
                }

                // Add the contents entity to the outer entity.
                entryElement.Add(contentElement);

                return(entryElement);
            }
            else
            {
                // Write the at:deleted-entry tombstone element
                XElement tombstoneElement = new XElement(FormatterConstants.AtomDeletedEntryNamespace + FormatterConstants.AtomDeletedEntryElementName);
                tombstoneElement.Add(new XElement(FormatterConstants.AtomNamespaceUri + FormatterConstants.AtomReferenceElementName, live.ServiceMetadata.Id));
                tombstoneElement.Add(new XElement(FormatterConstants.SyncNamespace + FormatterConstants.AtomPubCategoryElementName, typeName));
                return(tombstoneElement);
            }
        }
 /// <summary>
 /// Called to add a particular Entity
 /// </summary>
 /// <param name="entry">Entity to add to serialize to the stream</param>
 /// <param name="tempId">TempId for the Entity</param>
 public virtual void AddItem(IOfflineEntity entry, string tempId)
 {
     this.AddItem(entry, tempId, false);
 }
Пример #51
0
        //Note: Removed ref here
        private void AddEntityToDataSet(IOfflineEntity objectToRead, DataSet dataSet, string tableName)
        {
            Type t = objectToRead.GetType();

            PropertyInfo[] properties = t.GetProperties();

            Dictionary <string, string> globalToLocalMappingInfo = _globalToLocalPropertyMapping[t];

            Dictionary <string, string> localToGlobalMappingInfo = _localToGlobalPropertyMapping[t];

            // We need to create the table if it doesn't already exist
            DataTable dataTable = dataSet.Tables[tableName];

            if (dataTable == null)
            {
                dataSet.Tables.Add(tableName);

                dataTable = dataSet.Tables[tableName];

                // Create the columns of the table based off the
                // properties we reflected from the type
                foreach (PropertyInfo property in properties)
                {
                    // Do not add service related properties.
                    if (IsOfflineEntityServiceProperty(property.PropertyType))
                    {
                        continue;
                    }

                    Type type = property.PropertyType;
                    if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable <>))
                    {
                        type = property.PropertyType.GetGenericArguments()[0];
                    }

                    if (globalToLocalMappingInfo.ContainsKey(property.Name))
                    {
                        dataTable.Columns.Add(globalToLocalMappingInfo[property.Name], type);
                    }
                    else
                    {
                        dataTable.Columns.Add(property.Name, type);
                    }
                }

                // SQL Provider does not set primary keys on DataTable. So set Primary Keys if its not set
                ReflectionUtility.SetDataTablePrimaryKeys(dataTable, objectToRead.GetType(), globalToLocalMappingInfo);
            }

            dataTable.BeginLoadData();

            // Now the table should exist so add records to it.
            var columnArray = new object[dataTable.Columns.Count];

            for (int i = 0; i <= columnArray.Length - 1; i++)
            {
                // The property name to be set on the IOfflineEntity is the ColumnName unless there is a
                // SyncEntityPropertyMappingAttribute for the property in which case the name of that property is used instead.
                string colName = dataTable.Columns[i].ColumnName;
                if (localToGlobalMappingInfo.ContainsKey(colName))
                {
                    colName = localToGlobalMappingInfo[colName];
                }

                if (objectToRead.ServiceMetadata.IsTombstone && !dataTable.PrimaryKey.Contains(dataTable.Columns[i]))
                {
                    columnArray[i] = DBNull.Value;
                }
                else
                {
                    columnArray[i] = t.InvokeMember(colName,
                                                    BindingFlags.GetProperty, null,
                                                    objectToRead, new object[0]);
                    if (columnArray[i] != null)
                    {
                        if (columnArray[i].Equals(new Guid("00000000-0000-0000-0000-000000000000")))
                        {
                            columnArray[i] = DBNull.Value;
                        }
                    }
                }
            }

            // Add the row to the table in the dataset
            DataRow row = dataTable.LoadDataRow(columnArray, true);

            // If the entity is a tombstone, set the DataRowState property by calling the Delete or SetAdded method.
            if (objectToRead.ServiceMetadata.IsTombstone)
            {
                row.Delete();
            }
            else
            {
                row.SetAdded();
            }

            dataTable.EndLoadData();
        }
Пример #52
0
        /// <summary>
        /// This writes the public contents of the Entity to the passed in XElement.
        /// </summary>
        /// <param name="contentElement">The XElement to which the type properties is added to</param>
        /// <param name="entity">Entity</param>
        /// <returns>XElement representation of the properties element</returns>
        void WriteEntityContentsToElement(XElement contentElement, IOfflineEntity entity)
        {
            PropertyInfo[] properties = ReflectionUtility.GetPropertyInfoMapping(entity.GetType());

            // Write individual properties to the feed,
            foreach (PropertyInfo fi in properties)
            {
                object objValue = fi.GetValue(entity, null);
                if (objValue == null)
                {
                    contentElement.Add(
                        new XElement(fi.Name,
                                     new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Null),
                                     objValue));
                }
                else if (fi.PropertyType == FormatterConstants.CharType ||
                         fi.PropertyType == FormatterConstants.StringType ||
                         fi.PropertyType == FormatterConstants.GuidType)
                {
                    contentElement.Add(
                        new XElement(fi.Name,
                                     new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                                     objValue));
                }
                else if (fi.PropertyType == FormatterConstants.DateTimeType ||
                         fi.PropertyType == FormatterConstants.TimeSpanType ||
                         fi.PropertyType == FormatterConstants.DateTimeOffsetType)
                {
                    contentElement.Add(
                        new XElement(fi.Name,
                                     new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                                     FormatterUtilities.ConvertDateTimeForType_Json(objValue, fi.PropertyType)));
                }
                else if (fi.PropertyType == FormatterConstants.BoolType)
                {
                    contentElement.Add(
                        new XElement(fi.Name,
                                     new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Boolean),
                                     objValue));
                }
                else if (fi.PropertyType == FormatterConstants.ByteArrayType)
                {
                    byte[] bytes = (byte[])objValue;
                    contentElement.Add(
                        new XElement(fi.Name,
                                     new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                                     Convert.ToBase64String(bytes)));
                }
                else if (fi.PropertyType.IsGenericType && fi.PropertyType.Name.Equals(FormatterConstants.NullableTypeName, StringComparison.InvariantCulture))
                {
                    // Its a Nullable<T> property
                    Type genericParamType = fi.PropertyType.GetGenericArguments()[0];

                    string elementType = JsonElementTypes.Number;
                    if (genericParamType == FormatterConstants.BoolType)
                    {
                        elementType = JsonElementTypes.Boolean;
                    }
                    else if (genericParamType == FormatterConstants.DateTimeType ||
                             genericParamType == FormatterConstants.TimeSpanType ||
                             genericParamType == FormatterConstants.DateTimeOffsetType)
                    {
                        contentElement.Add(
                            new XElement(fi.Name,
                                         new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.String),
                                         FormatterUtilities.ConvertDateTimeForType_Json(objValue, genericParamType)));
                        continue;
                    }
                    else if (genericParamType == FormatterConstants.CharType ||
                             genericParamType == FormatterConstants.GuidType)
                    {
                        elementType = JsonElementTypes.String;
                    }

                    contentElement.Add(
                        new XElement(fi.Name,
                                     new XAttribute(FormatterConstants.JsonTypeAttributeName, elementType),
                                     objValue));
                }
                else // Its a number
                {
                    contentElement.Add(
                        new XElement(fi.Name,
                                     new XAttribute(FormatterConstants.JsonTypeAttributeName, JsonElementTypes.Number),
                                     objValue));
                }
            }
        }
        /// <summary>
        /// Copies the individual properties from the entity back in to the DataTable's first row.
        /// This should be used only when merging a user conflict resolution back in to a DataRow.
        /// This returns the merged results as an object array
        /// </summary>
        /// <param name="entity">Entity from which to read values</param>
        /// <param name="table">The Table whose first DataRow will be updated.</param>
        /// <returns>The contents of the DataRow as an object array</returns>
        internal object[] CopyEntityToDataRow(IOfflineEntity entity, DataTable table)
        {
            Debug.Assert(table.Rows.Count == 1, "table.Rows.Count ==1");
            if (table.Rows.Count != 1)
            {
                throw new InvalidOperationException("Cannot copy Entity to a DataTable whose row count != 1");
            }

            Dictionary<string, string> mappingInfo = _localToGlobalPropertyMapping[entity.GetType()];

            // Check for tombstones
            bool isRowDeleted = false;
            if (table.Rows[0].RowState == DataRowState.Deleted)
            {
                isRowDeleted = true;
                table.Rows[0].RejectChanges();
            }


            // Retrieve the current row values
            object[] rowValues = table.Rows[0].ItemArray;
            PropertyInfo[] properties = entity.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            for (int i = 0; i < table.Columns.Count; i++)
            {
                // Iterate over each non sync column and read its value from the entity
                if (IsSyncSpecificColumn(table.Columns[i].ColumnName))
                {
                    continue;
                }

                // Read the value of the property
                string columnName = null;
                mappingInfo.TryGetValue(table.Columns[i].ColumnName, out columnName);
                columnName = columnName ?? table.Columns[i].ColumnName;
         
                // Retrieve the PropertyInfo
                PropertyInfo info = properties.Where(e => e.Name.Equals(columnName, StringComparison.Ordinal)).FirstOrDefault();

                Debug.Assert(info != null, "PropertyInfo is not null.");
                // Get the property's value and put it in the object array
                rowValues[i] = info.GetValue(entity, null) ?? DBNull.Value;
            }

            // Write the row values back to the DataRow
            table.Rows[0].ItemArray = rowValues;

            if (isRowDeleted)
            {
                table.Rows[0].Delete();
            }
            return rowValues;
        }
Пример #54
0
        /// <summary>
        /// Build the OData Atom primary keystring representation
        /// </summary>
        /// <param name="live">Entity for which primary key is required</param>
        /// <returns>String representation of the primary key</returns>
        public static string GetPrimaryKeyString(IOfflineEntity live)
        {
            StringBuilder builder = new StringBuilder();

            string sep = string.Empty;
            foreach (PropertyInfo keyInfo in GetPrimaryKeysPropertyInfoMapping(live.GetType()))
            {
                if (keyInfo.PropertyType == FormatterConstants.GuidType)
                    builder.AppendFormat("{0}{1}=guid'{2}'", sep, keyInfo.Name, keyInfo.GetValue(live, null));
                else if (keyInfo.PropertyType == FormatterConstants.StringType)
                    builder.AppendFormat("{0}{1}='{2}'", sep, keyInfo.Name, keyInfo.GetValue(live, null));
                else
                    builder.AppendFormat("{0}{1}={2}", sep, keyInfo.Name, keyInfo.GetValue(live, null));

                if(string.IsNullOrEmpty(sep))
                    sep = ", ";

            }
            return builder.ToString();
        }
        internal void GetEntityFromDataRow(DataColumnCollection columnCollection, DataRow row, IOfflineEntity objectToConvert)
        {
            Type t = objectToConvert.GetType();
            Dictionary<string, string> mappingInfo = _localToGlobalPropertyMapping[t];

            bool isDeleted = false;
            if (row.RowState == DataRowState.Deleted)
            {
                isDeleted = true;
                row.RejectChanges();
            }

            // Note: Call BeginEdit only after check for Deleted row state, 
            // otherwise this call will crash.
            row.BeginEdit();

            for (Int32 i = 0; i <= columnCollection.Count - 1; i++)
            {
                if (IsSyncSpecificColumn(columnCollection[i].ColumnName))
                {
                    continue;
                }

                //NOTE: the datarow column names must match exactly (including case) to the IOfflineEntity's property names
                object columnValue = row[columnCollection[i].ColumnName];

                if (DBNull.Value != columnValue)
                {
                    t.InvokeMember((mappingInfo.ContainsKey(columnCollection[i].ColumnName)) 
                                        ? mappingInfo[columnCollection[i].ColumnName]  
                                        : columnCollection[i].ColumnName,
                                   BindingFlags.SetProperty, null,
                                   objectToConvert,
                                   new[] {columnValue});
                }
            }

            if (isDeleted)
            {
                row.Delete();

                // Mark the IsTombstone field if the RowState was deleted.
                objectToConvert.ServiceMetadata.IsTombstone = true;
            }

            row.EndEdit();
        }
 internal void AddUpdatedItem(IOfflineEntity item)
 {
     this._updatedItems.Add(item);
 }
        //Note: Removed ref here
        private void AddEntityToDataSet(IOfflineEntity objectToRead, DataSet dataSet, string tableName)
        {
            Type t = objectToRead.GetType();
            PropertyInfo[] properties = t.GetProperties();

            Dictionary<string, string> globalToLocalMappingInfo = _globalToLocalPropertyMapping[t];

            Dictionary<string, string> localToGlobalMappingInfo = _localToGlobalPropertyMapping[t];

            // We need to create the table if it doesn't already exist
            DataTable dataTable = dataSet.Tables[tableName];

            if (dataTable == null)
            {            
                dataSet.Tables.Add(tableName);

                dataTable = dataSet.Tables[tableName];

                // Create the columns of the table based off the 
                // properties we reflected from the type
                foreach (PropertyInfo property in properties)
                {
                    // Do not add service related properties.
                    if (IsOfflineEntityServiceProperty(property.PropertyType))
                    {
                        continue;
                    }

                    Type type = property.PropertyType;
                    if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                    {
                        type = property.PropertyType.GetGenericArguments()[0];
                    }

                    if (globalToLocalMappingInfo.ContainsKey(property.Name))
                    {
                        dataTable.Columns.Add(globalToLocalMappingInfo[property.Name], type);
                    }
                    else
                    {
                        dataTable.Columns.Add(property.Name, type);
                    }
                }

                // SQL Provider does not set primary keys on DataTable. So set Primary Keys if its not set
                ReflectionUtility.SetDataTablePrimaryKeys(dataTable, objectToRead.GetType(), globalToLocalMappingInfo);
            }

            dataTable.BeginLoadData();

            // Now the table should exist so add records to it.
            var columnArray = new object[dataTable.Columns.Count];

            for (int i = 0; i <= columnArray.Length - 1; i++)
            {
                // The property name to be set on the IOfflineEntity is the ColumnName unless there is a
                // SyncEntityPropertyMappingAttribute for the property in which case the name of that property is used instead.
                string colName = dataTable.Columns[i].ColumnName;
                if (localToGlobalMappingInfo.ContainsKey(colName))
                {
                    colName = localToGlobalMappingInfo[colName];
                }

                if (objectToRead.ServiceMetadata.IsTombstone && !dataTable.PrimaryKey.Contains(dataTable.Columns[i]))
                {
                    columnArray[i] = DBNull.Value;
                }
                else
                {
                    columnArray[i] = t.InvokeMember(colName,
                                                BindingFlags.GetProperty, null,
                                                objectToRead, new object[0]);
                    if (columnArray[i] != null)
                    {
                        if (columnArray[i].Equals(new Guid("00000000-0000-0000-0000-000000000000")))
                            columnArray[i] = DBNull.Value;
                    }
                }
            }

            // Add the row to the table in the dataset
            DataRow row = dataTable.LoadDataRow(columnArray, true);

            // If the entity is a tombstone, set the DataRowState property by calling the Delete or SetAdded method.
            if (objectToRead.ServiceMetadata.IsTombstone)
            {
                row.Delete();
            }
            else
            {
                row.SetAdded();
            }

            dataTable.EndLoadData();
        }
        /// <summary>
        /// Generate and save the SyncId of the LiveEntity. 
        /// This value is used later after all changes are applied to project on the latest 
        /// server knowledge and add positive exceptions to the updated client knowledge that is sent in the response.
        /// </summary>
        /// <param name="tableName">Table name that the entity represents</param>
        /// <param name="c">Conflicting entity for which we need to save the SyncId.</param>
        private SyncId GenerateSyncIdForConflictingEntity(string tableName, IOfflineEntity c)
        {
            Debug.Assert(null != c, "null != c");

            var pkValues = new List<object>();

            // Get the primary key values from the LiveEntity
            Type entityType = c.GetType();

            // The ordering of keys here is assumed to be the same order in which SyncId's are generated.
            // Otherwise, behavior is undefined and incorrect positive exceptions are added.
            PropertyInfo[] primaryKeyPropertyInfoMapping = ReflectionUtility.GetPrimaryKeysPropertyInfoMapping(entityType);

            foreach (var propertyInfo in primaryKeyPropertyInfoMapping)
            {
                pkValues.Add(propertyInfo.GetValue(c, null));
            }

            // Generate the SyncId for the conflicting item.
            SyncId rowId = SyncUtil.InitRowId(tableName, pkValues);

            return rowId;
        }
Пример #59
0
        /// <summary>
        /// Writes the <entry/> tag and all its related elements.
        /// </summary>
        /// <param name="live">Actual entity whose value is to be emitted.</param>
        /// <param name="tempId">The temporary Id if any</param>
        /// <param name="emitPartial">Bool flag that denotes whether a partial metadata only entity is to be written</param>
        /// <returns>XElement representation of the entry element</returns>
        private XElement WriteEntry(IOfflineEntity live, string tempId, bool emitPartial)
        {
            string typeName = live.GetType().FullName;

            if (!live.ServiceMetadata.IsTombstone)
            {
                var entryElement = new XElement(FormatterConstants.AtomXmlNamespace + C.Entry);
                entryElement.Add(new XAttribute(C.Caption, typeName));

                if (!emitPartial)
                {
                    // Write the entity contents
                    WriteEntityContents(entryElement, live);
                }

                return entryElement;
            }
            // Write the at:deleted-entry tombstone element
            var tombstoneElement = new XElement(FormatterConstants.AtomXmlNamespace + C.Tombstone, new XAttribute(C.Caption, typeName));
            Guid id;
            if (!Guid.TryParse(live.ServiceMetadata.Id, out id))
            {
                string[] split = live.ServiceMetadata.Id.Split('\'');
                id = Guid.Parse(split[1]);
            }
            tombstoneElement.Add(id.ToString());
            return tombstoneElement;
        }