Sync101 with Custom Filtering

Shows how to track custom filters and how to use a standard custom provider to send changes from a filter-tracking replica to two different filtered replicas.

C# (58.2 KB)
 
 
 
 
 
(0)
1,322 times
Add to favorites
5/13/2011
E-mail Twitter del.icio.us Digg Facebook
using System;
using System.Collections.Generic;
using Microsoft.Synchronization;
using System.Text;


namespace CustomFilterSyncSample
{
    public class BaseProvider<T>
        : KnowledgeSyncProvider,
          INotifyingChangeApplierTarget,
          IChangeDataRetriever where T:ICloneable
    {
        public BaseProvider(
            Guid replicaId, 
            IDataItemOperations<T> iUpdatable) 
            : base()       
        {
            _idFormatGroup = new SyncIdFormatGroup();
            _idFormatGroup.ItemIdFormat.Length = 16;
            _replicaId = new SyncId(replicaId);
            _store = new DataStore<T>(iUpdatable);
            _knowledge = new SyncKnowledge(_idFormatGroup, _replicaId, _store.TickCount);
            _forgottenKnowledge = new ForgottenKnowledge(_idFormatGroup, _knowledge);
            _iUpdatable = iUpdatable;                   
        }
                

        #region KnowledgeSyncProvider
        //
        // KnowledgeSyncProvider
        //

        //
        // KSP properties
        //

        public override SyncIdFormatGroup IdFormats
        {
            get
            {
                return _idFormatGroup;
            }
        }

        //
        // KSP methods
        //

        public override void BeginSession(
            SyncProviderPosition position, 
            SyncSessionContext syncSessionContext)
        {
            _syncSessionContext = syncSessionContext;
        }

        public override void EndSession(
            SyncSessionContext syncSessionContext)
        {
            _syncSessionContext = null;
        }

        public override void GetSyncBatchParameters(
            out uint batchSize, 
            out SyncKnowledge knowledge)
        {
            batchSize = FilteredReplicaProvider<T>._batchSize;
            knowledge = _knowledge;
        }

        public override ChangeBatch GetChangeBatch(
            uint batchSize,
            SyncKnowledge destinationKnowledge,
            out object changeDataRetriever)
        {
            throw new NotImplementedException("Derived classes need to provide the implementation");
        }

        /// <summary>
        /// Change Application
        /// </summary>
        /// <param name="resolutionPolicy"></param>
        /// <param name="sourceChanges"></param>
        /// <param name="changeDataRetriever"></param>
        /// <param name="syncCallbacks"></param>
        /// <param name="sessionStatistics"></param>
        public override void ProcessChangeBatch(
            ConflictResolutionPolicy resolutionPolicy,
            ChangeBatch sourceChanges,
            object changeDataRetriever,
            SyncCallbacks syncCallbacks,
            SyncSessionStatistics sessionStatistics)
        {
            NotifyingChangeApplier changeApplier = new NotifyingChangeApplier(_idFormatGroup);

            changeApplier.ApplyChanges(
                resolutionPolicy,
                sourceChanges,
                changeDataRetriever as IChangeDataRetriever,
                _knowledge,
                _forgottenKnowledge,
                this, // INotifyingChangeApplierTarget
                _syncSessionContext,
                syncCallbacks);
        }

        /// <summary>
        /// This sample does not implement tombstone or ghost cleanup. Hence Full enumeration
        /// is not implemented
        /// </summary>
        /// <param name="batchSize"></param>
        /// <param name="lowerEnumerationBound"></param>
        /// <param name="knowledgeForDataRetrieval"></param>
        /// <param name="changeDataRetriever"></param>
        /// <returns></returns>
        public override FullEnumerationChangeBatch GetFullEnumerationChangeBatch(
            uint batchSize,
            SyncId lowerEnumerationBound,
            SyncKnowledge knowledgeForDataRetrieval,
            out object changeDataRetriever)
        {
            throw new NotImplementedException();
        }


        /// <summary>
        /// This sample does not implement tombstone or ghost cleanup. Hence Full enumeration
        /// is not implemented
        /// </summary>
        /// <param name="resolutionPolicy"></param>
        /// <param name="sourceChanges"></param>
        /// <param name="changeDataRetriever"></param>
        /// <param name="syncCallbacks"></param>
        /// <param name="sessionStatistics"></param>
        public override void ProcessFullEnumerationChangeBatch(
            ConflictResolutionPolicy resolutionPolicy,
            FullEnumerationChangeBatch sourceChanges,
            object changeDataRetriever,
            SyncCallbacks syncCallbacks,
            SyncSessionStatistics sessionStatistics)
        {
            throw new NotImplementedException();
        }
        #endregion KnowledgeSyncProvider
        
        #region INotifyingChangeApplierTarget
        public ulong GetNextTickCount()
        {
            return _store.GetNextTickCount();
        }

        public IChangeDataRetriever GetDataRetriever()
        {
            return this;
        }

        public virtual bool TryGetDestinationVersion(
            ItemChange sourceChange, 
            out ItemChange destinationVersion)
        {
            destinationVersion = null;
            SyncableItem<T> item;

            if (_store.TryGetValue(sourceChange.ItemId, out item))
            {
                if (!item.IsDeleted)
                {                                     
                    //
                    // Item is alive or ghost
                    //
                    destinationVersion = new ItemChange(
                                            _idFormatGroup,
                                            _replicaId,
                                            item.GlobalId,
                                            ChangeKind.Update,
                                            item.CreationVersion,
                                            item.CurrentVersion);

                    foreach (uint filterIndex in item.FilterChanges.Keys)
                    {
                        destinationVersion.AddFilterChange(filterIndex, item.FilterChanges[filterIndex]);
                    }
                                       
                }
                else
                {
                    destinationVersion = new ItemChange(
                                            _idFormatGroup,
                                            _replicaId,
                                            item.GlobalId,
                                            ChangeKind.Deleted,
                                            item.CreationVersion,
                                            item.CurrentVersion);
                }
            }

            return destinationVersion != null;
        }

        public virtual void SaveItemChange(
            SaveChangeAction saveChangeAction,
            ItemChange change,
            SaveChangeContext context)
        {
            throw new NotImplementedException("Derived classes need to provide the implementation");
        }

        public virtual void SaveChangeWithChangeUnits(
            ItemChange change, 
            SaveChangeWithChangeUnitsContext context)
        {
            throw new NotImplementedException("Derived classes need to provide the implementation");
        }

        // Called for conflicts to be logged for later resolution
        public void SaveConflict(
            ItemChange conflictingChange, 
            object conflictingChangeData, 
            SyncKnowledge conflictingChangeKnowledge)
        {
            throw new NotImplementedException();
        }


        /// <summary>
        /// No need to implement this because SaveKnowledgeWithFilterForgottenKnowledge will be called  
        /// INotifyingChangeApplierTarget that is also IFilterTrackingNotifyingChangeApplierTarget
        /// </summary>
        /// <param name="knowledge"></param>
        /// <param name="forgottenKnowledge"></param>
        public virtual void StoreKnowledgeForScope(
            SyncKnowledge knowledge, 
            ForgottenKnowledge forgottenKnowledge)
        { 
            throw new NotImplementedException("Derived classes need to provide the implementation");
        }
        #endregion INotifyingChangeApplierTarget
                
        #region IChangeDataRetriever
        public object LoadChangeData(
            LoadChangeContext loadChangeContext)
        {
            return _store.GetSyncableItem(loadChangeContext.ItemChange.ItemId).Item;
        }
        #endregion IChangeDataRetriever

        #region Test helper methods
        public int ItemCount
        {
            get
            {
                return _store.Count;
            }
        }
        
        public SortedDictionary<SyncId, SyncableItem<T>>.Enumerator GetEnumerator()
        {
            return _store.GetEnumerator();
        }

        public SyncableItem<T> CreateItem(Guid itemId)
        {
            // Create
            return _store.CreateItem(itemId);
        }

        public SyncableItem<T> GetItem(
            SyncId itemId)
        {
            SyncableItem<T> item;
            _store.TryGetValue(itemId, out item);
            return item;
        }
        
        public void UpdateItem(
            SyncableItem<T> item, 
            object value)
        {
            _iUpdatable.UpdateItem(item.Item, value);
        }
        
        public virtual void PrepareForSync()
        {
            _knowledge.SetLocalTickCount(_store.TickCount);
        }

        public override string ToString()
        {            
            return _store.ToString();
        }

        public string ItemsDataToString()
        {
            return _store.ItemsDataToString();
        }
        #endregion Test helper methods

        #region Protected methods
        protected void SaveKnowledges(
                SyncKnowledge knowledge,
                ForgottenKnowledge forgottenKnowledge)
        {            
            //
            // If knowledge can change then do a union
            //
            _knowledge = knowledge;

            if (forgottenKnowledge != null)
            {
                _forgottenKnowledge = forgottenKnowledge;
            }
        }


        /// <summary>
        /// This method is called from the Derived class's SaveChangeWithChangeUnits. 
        /// See Derived Class's SaveChangeWithChangeUnits
        /// </summary>
        /// <param name="saveChangeAction"></param>
        /// <param name="change"></param>
        /// <param name="context"></param>
        protected void SaveItemChanges(
            SaveChangeAction saveChangeAction,
            ItemChange change,
            SaveChangeContext context)
        {
            switch (saveChangeAction)
            {
                case SaveChangeAction.Create:
                    _store.AddItem(change, (T)((T)context.ChangeData).Clone());                   
                    goto case SaveChangeAction.UpdateVersionOnly;

                case SaveChangeAction.UpdateVersionOnly:
                    _store.UpdateItem(change);
                    break;

                case SaveChangeAction.UpdateVersionAndData:
                    _store.UpdateItem(change, ((T)context.ChangeData).Clone());
                    break;

                case SaveChangeAction.UpdateVersionAndMergeData:
                    throw new NotImplementedException();

                default:
                    throw new InvalidOperationException("Unexpected saveChangeAction " + saveChangeAction.ToString());
            }            
        }
        #endregion Protected methods        

        #region private fields
        private IDataItemOperations<T> _iUpdatable;
        private const uint _batchSize = 10;
        private SyncId _replicaId;
        private SyncIdFormatGroup _idFormatGroup;
        private SyncKnowledge _knowledge;
        private ForgottenKnowledge _forgottenKnowledge;
        private SyncSessionContext _syncSessionContext;
        private uint _filterCount;

        //
        // Data
        //
        //protected SortedDictionary<SyncId, SyncableItem<T>> _items;
        private DataStore<T> _store;

        //
        // Member variables for change enumeration
        //
        private uint _sessionItemCount;
        #endregion private fields

        #region Protected fields
        protected SortedDictionary<SyncId, SyncableItem<T>>.Enumerator ItemDataEnumerator;

        protected SyncId ReplicaId
        {
            get
            {
                return _replicaId;
            }
            set
            {
                _replicaId = value;
            }
        }

        protected SyncIdFormatGroup IdFormatGroup
        {
            get
            {
                return _idFormatGroup;
            }
            set
            {
                _idFormatGroup = value;
            }
        }

        protected SyncKnowledge MyKnowledge
        {
            get
            {
                return _knowledge;
            }
            set
            {
                _knowledge = value;
            }
        }

        protected ForgottenKnowledge MyForgottenKnowledge
        {
            get
            {
                return _forgottenKnowledge;
            }
            set
            {
                _forgottenKnowledge = value;
            }
        }

        protected SyncSessionContext SessionContext
        {
            get
            {
                return _syncSessionContext;
            }
            set
            {
                _syncSessionContext = value;
            }
        }

        protected uint FilterCount
        {
            get
            {
                return _filterCount;
            }
            set
            {
                _filterCount = value;
            }
        }

        protected uint SessionCount
        {
            get
            {
                return _sessionItemCount;
            }
            set
            {
                _sessionItemCount = value;
            }
        }

        protected DataStore<T> MyStore
        {
            get
            {
                return _store;
            }
            set
            {
                _store = value;
            }
        }

        protected IDataItemOperations<T> ItemOperations
        {
            get
            {
                return _iUpdatable;
            }
            set
            {
                _iUpdatable = value;
            }
        }
        #endregion Protected fields
    } 
}