public bool ShouldCreateNewItemForConflict(RecordItemChange change, Exception ex)
        {
            ServerException se = ex as ServerException;
            if (se != null)
            {
                return (this.IsItemKeyNotFound(se)); // Sometimes when the item is really missing, we get this error..
            }

            return false;
        }
        public bool ShouldRetryCommit(RecordItemChange change, Exception ex)
        {
            if (this.IsClientValidationError(ex) ||
                this.IsSerializationError(ex) ||
                !this.IsHttpError(ex)
            )
            {
                return false;
            }
            
            if (this.MaxAttemptsPerChange > 0 && change.Attempt >= this.MaxAttemptsPerChange)
            {
                return false;
            }

            return true;
        }
 public static int Compare(RecordItemChange x, RecordItemChange y)
 {
     int cmp = x.Timestamp.CompareTo(y.Timestamp); 
     if (cmp == 0)
     {
         cmp = x.ItemID.CompareTo(y.ItemID);
     }
     return cmp;
 }
        internal async Task OnChangeCommittedAsync(RecordItemChange change)
        {
            if (change.ChangeType != RecordItemChangeType.Put)
            {
                return;
            }          
              
            try
            {
                SynchronizedType sType = await this.GetAsync(change.TypeID);

                await sType.OnPutCommittedAsync(change);
                
                this.TypeUpdated.SafeInvokeInUIThread(this, change.TypeID);
            }
            catch
            {
            }
        }
 internal async Task SaveChangeAsync(RecordItemChange change)
 {
     await m_changeStore.PutAsync(change.ItemID, change);
 }
        async Task<bool> CommitPutAsync(RecordItemChange change, long itemLockID)
        {
            IItemDataTyped item = await m_store.Local.GetByIDAsync(change.ItemID);
            if (item == null)
            {
                return false;
            }

            change.LocalData = item;
            if (item.Key.IsLocal)
            {
                await this.CommitNewAsync(change);
            }
            else
            {
                await this.CommitUpdateAsync(change);
            }
            //
            // Refetch the item from HealthVault, to get updated dates etc...
            //
            await this.RefreshItemAsync(change, itemLockID);

            return true;
        }
 void NotifyCommitFailed(RecordItemChange change)
 {
     Debug.WriteLine("Change Commit failed {0}", change);
     this.CommitFailed.SafeInvokeInUIThread(this, change);
 }
 async Task UpdateChangeAttemptCount(RecordItemChange change)
 {
     try
     {
         change.Attempt++;
         await m_changeTable.SaveChangeAsync(change);
     }
     catch(Exception ex)
     {
         this.NotifyError(ex);
     }
 }
 async Task NotifySynchronizedTypesAsync(RecordItemChange change)
 {
     try
     {
         if (this.SynchronizedTypes != null)
         {
             await this.SynchronizedTypes.OnChangeCommittedAsync(change);
         }
     }
     catch
     {
     }
 }
 async Task RefreshItemAsync(RecordItemChange change, long lockID)
 {
     change.UpdatedItem = null;
     try
     {
         change.UpdatedItem = await m_store.RemoteStore.GetItemAsync(change.UpdatedKey, m_store.SectionsToFetch);
     }
     catch(Exception ex)
     {
         this.NotifyError(ex);
     }
 }
        async Task<bool> DetectDuplicateAsync(RecordItemChange change)
        {            
            IList<RecordItem> results = await this.LookupCommitsInRemoteStoreAsync(change.ChangeID);
            if (results.IsNullOrEmpty())
            {
                return false;
            }

            change.UpdatedKey = results[0].Key;
            return true;
        }
 async Task CommitRemoveAsync(RecordItemChange change, long itemLockID)
 {
     try
     {
         if (change.Key.IsLocal)
         {
             return;
         }
         await m_store.RemoteStore.RemoveItemAsync(change.Key);
     }
     catch(ServerException se)
     {
         if (!m_errorHandler.IsItemKeyNotFound(se))
         {
             throw;
         }
     }
 }
 async Task CommitNewAsync(RecordItemChange change)
 {
     Debug.Assert(change.HasLocalData);
     change.UpdatedKey = await m_store.RemoteStore.NewAsync(change.LocalData);
 }
        async Task CommitUpdateAsync(RecordItemChange change)
        {
            Debug.Assert(change.HasLocalData);

            if (await this.DetectDuplicateAsync(change))
            {
                // Already applied this change
                return;
            }                

            try
            {
                change.UpdatedKey = await m_store.RemoteStore.PutAsync(change.LocalData);
                return;
            }
            catch (Exception ex)
            {
                if (!m_errorHandler.ShouldCreateNewItemForConflict(change, ex))
                {
                    //
                    // Let the calling function decide how to handle all other scenarios
                    //
                    throw;
                }
            }
            //
            // Conflict resolution is simple. We don't want to lose data. If the data on the server changed from underneath us,
            // we will write this item as a NEW one, since the user did actually take the trouble to create the entry
            //
            await this.CommitNewAsync(change);
        }
        public static RecordItemChange UpdateChange(string typeID, ItemKey key, RecordItemChangeType changeType, RecordItemChange existingChange)
        {
            if (existingChange == null)
            {
                return new RecordItemChange(typeID, key, changeType);
            }

            if (existingChange.ChangeType == RecordItemChangeType.Remove && changeType == RecordItemChangeType.Put)
            {
                // Can't put an item already marked for deletion
                throw new StoreException(StoreErrorNumber.ItemAlreadyDeleted);
            }

            if (existingChange.TypeID != typeID)
            {
                // Defensive code. Should never happen
                throw new StoreException(StoreErrorNumber.TypeIDMismatch);
            }

            existingChange.InitChange(key, changeType);

            return existingChange;
        }
 void NotifyCommitSuccess(RecordItemChange change)
 {
     Debug.WriteLine("Change Committed {0}", change);
     this.CommitSuccess.SafeInvokeInUIThread(this, change);
 }
        internal async Task OnPutCommittedAsync(RecordItemChange change)
        {
            Debug.Assert(change.HasUpdatedItem || change.HasLocalData);

            RecordItem updatedItem = change.UpdatedItem;
            if (updatedItem == null)
            {
                updatedItem = change.LocalData.Item;
                updatedItem.Key = change.UpdatedKey;
            }

            await this.Data.Local.PutItemAsync(updatedItem);
            await this.UpdateKeyAsync(change.ItemID, updatedItem);
        }
        // Assumes that you've acquire the item lock earlier! 
        // Critical that the item lock be acquired
        async Task<bool> CommitChange(RecordItemChange change, long itemLockID)
        { 
            bool dequeue = false;
            try
            {
                await this.UpdateChangeAttemptCount(change);

                if (change.ChangeType == RecordItemChangeType.Remove)
                {
                    await this.CommitRemoveAsync(change, itemLockID);
                }
                else
                {
                    if (await this.CommitPutAsync(change, itemLockID))
                    {
                        await this.NotifySynchronizedTypesAsync(change);
                    }
                }
                
                this.NotifyCommitSuccess(change);

                dequeue = true;
            }
            catch (Exception ex)
            {
                if (m_errorHandler.IsHaltingError(ex))
                {
                    this.NotifyError(ex);
                    return false; // Stop processing items right away because of major Halting errors, such as Network
                }

                if (m_errorHandler.ShouldRetryCommit(change, ex))
                {
                    // Leave the change in the commit queue. try again later
                    this.NotifyError(ex);
                }
                else
                {
                    this.NotifyCommitFailed(change);
                    dequeue = true;
                }
            }

            if (dequeue)
            {
                await m_changeTable.RemoveChangeAsync(change.ItemID);
            }

            return true;
        }