public async Task Synchronize(ISynchronizationLogger logger, TContext synchronizationContext)
        {
            s_logger.InfoFormat("Entered. Syncstrategy '{0}' with Atype='{1}' and Btype='{2}'", _initialSyncStateCreationStrategy.GetType().Name, typeof(TAtypeEntity).Name, typeof(TBtypeEntity).Name);

            using (var totalProgress = _totalProgressFactory.Create())
            {
                var knownEntityRelations = _entityRelationDataAccess.LoadEntityRelationData()
                                           ?? new IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion>[] { };

                var stateTokens = _stateTokenDataAccess.LoadKnownStateTokens();

                using (var interceptor = _synchronizationInterceptorFactory.Create())
                {
                    var aStatesTask = _atypeStateAwareEntityRepository.GetFullRepositoryState(knownEntityRelations.Select(r => r.AtypeId), stateTokens.AToken, synchronizationContext, logger.AGetVersionsEntityLogger);
                    var bStatesTask = _btypeStateAwareEntityRepository.GetFullRepositoryState(knownEntityRelations.Select(r => r.BtypeId), stateTokens.BToken, synchronizationContext, logger.BGetVersionsEntityLogger);

                    (var aStates, var newAToken) = await aStatesTask;
                    (var bStates, var newBToken) = await bStatesTask;

                    await Synchronize(
                        totalProgress,
                        knownEntityRelations,
                        aStates,
                        bStates,
                        logger,
                        synchronizationContext,
                        interceptor,
                        newEntityRelations => _entityRelationDataAccess.SaveEntityRelationData(newEntityRelations));

                    _stateTokenDataAccess.SaveKnownStateTokens(newAToken, newBToken);
                }
            }
            s_logger.DebugFormat("Exiting.");
        }
        public async Task Synchronize(ISynchronizationLogger logger, TContext synchronizationContext)
        {
            s_logger.InfoFormat("Entered. Syncstrategy '{0}' with Atype='{1}' and Btype='{2}'", _initialSyncStateCreationStrategy.GetType().Name, typeof(TAtypeEntity).Name, typeof(TBtypeEntity).Name);

            using (var totalProgress = _totalProgressFactory.Create())
            {
                var knownEntityRelations = _entityRelationDataAccess.LoadEntityRelationData()
                                           ?? new IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion>[] { };

                using (var interceptor = _synchronizationInterceptorFactory.Create())
                {
                    var newAVersionsTask = _atypeRepository.GetAllVersions(knownEntityRelations.Select(r => r.AtypeId), synchronizationContext);
                    var newBVersionsTask = _btypeRepository.GetAllVersions(knownEntityRelations.Select(r => r.BtypeId), synchronizationContext);

                    var newAVersions = CreateDictionary(
                        await newAVersionsTask,
                        _atypeIdComparer);

                    var newBVersions = CreateDictionary(
                        await newBVersionsTask,
                        _btypeIdComparer);

                    await Synchronize(
                        totalProgress,
                        knownEntityRelations,
                        newAVersions,
                        newBVersions,
                        logger,
                        synchronizationContext,
                        interceptor,
                        newEntityRelations => _entityRelationDataAccess.SaveEntityRelationData(newEntityRelations));
                }
            }
            s_logger.DebugFormat("Exiting.");
        }
        public async Task <bool> Synchronize()
        {
            s_logger.InfoFormat("Entered. Syncstrategy '{0}' with Atype='{1}' and Btype='{2}'", _initialSyncStateCreationStrategy.GetType().Name, typeof(TAtypeEntity).Name, typeof(TBtypeEntity).Name);


            try
            {
                using (var totalProgress = _totalProgressFactory.Create())
                {
                    var cachedData = _entityRelationDataAccess.LoadEntityRelationData();

                    var aVersionsTask = _atypeRepository.GetVersions();
                    var bVersionsTask = _btypeRepository.GetVersions();

                    var aVersions = await aVersionsTask;
                    var bVersions = await bVersionsTask;

                    var atypeRepositoryVersions = CreateDictionary(
                        aVersions,
                        _atypeIdComparer);

                    var btypeRepositoryVersions = CreateDictionary(
                        bVersions,
                        _btypeIdComparer);

                    IReadOnlyDictionary <TAtypeEntityId, TAtypeEntity> aEntities = null;
                    IReadOnlyDictionary <TBtypeEntityId, TBtypeEntity> bEntities = null;

                    try
                    {
                        if (cachedData == null)
                        {
                            s_logger.Info("Did not find entity caches. Performing initial population");

                            totalProgress.NotifyLoadCount(atypeRepositoryVersions.Count, btypeRepositoryVersions.Count);

                            using (totalProgress.StartARepositoryLoad())
                            {
                                aEntities = CreateDictionary(
                                    await _atypeRepository.Get(atypeRepositoryVersions.Keys),
                                    _atypeIdComparer);
                            }

                            using (totalProgress.StartBRepositoryLoad())
                            {
                                bEntities = CreateDictionary(
                                    await _btypeRepository.Get(btypeRepositoryVersions.Keys),
                                    _btypeIdComparer);
                            }

                            cachedData = _initialEntityMatcher.FindMatchingEntities(
                                _entityRelationDataFactory,
                                aEntities,
                                bEntities,
                                atypeRepositoryVersions,
                                btypeRepositoryVersions);
                        }

                        var entitySyncStates = new EntitySyncStateContainer <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity>();

                        var aDeltaLogInfo = new VersionDeltaLoginInformation();
                        var bDeltaLogInfo = new VersionDeltaLoginInformation();

                        foreach (var cachedEntityData in cachedData)
                        {
                            TAtypeEntityVersion repositoryAVersion;
                            TBtypeEntityVersion repositoryBVersion;

                            var repositoryAVersionAvailable = atypeRepositoryVersions.TryGetValue(cachedEntityData.AtypeId, out repositoryAVersion);
                            var repositoryBVersionAvailable = btypeRepositoryVersions.TryGetValue(cachedEntityData.BtypeId, out repositoryBVersion);

                            if (repositoryAVersionAvailable)
                            {
                                atypeRepositoryVersions.Remove(cachedEntityData.AtypeId);
                            }

                            if (repositoryBVersionAvailable)
                            {
                                btypeRepositoryVersions.Remove(cachedEntityData.BtypeId);
                            }

                            var entitySyncState = CreateInitialSyncState(cachedEntityData, repositoryAVersionAvailable, repositoryAVersion, repositoryBVersionAvailable, repositoryBVersion, aDeltaLogInfo, bDeltaLogInfo);

                            entitySyncStates.Add(entitySyncState);
                        }

                        aDeltaLogInfo.IncAdded(atypeRepositoryVersions.Count);
                        bDeltaLogInfo.IncAdded(btypeRepositoryVersions.Count);

                        s_logger.InfoFormat("Atype delta: {0}", aDeltaLogInfo);
                        s_logger.InfoFormat("Btype delta: {0}", bDeltaLogInfo);

                        foreach (var newA in atypeRepositoryVersions)
                        {
                            entitySyncStates.Add(_initialSyncStateCreationStrategy.CreateFor_Added_NotExisting(newA.Key, newA.Value));
                        }

                        foreach (var newB in btypeRepositoryVersions)
                        {
                            entitySyncStates.Add(_initialSyncStateCreationStrategy.CreateFor_NotExisting_Added(newB.Key, newB.Value));
                        }

                        HashSet <TAtypeEntityId> aEntitesToLoad = new HashSet <TAtypeEntityId>();
                        HashSet <TBtypeEntityId> bEntitesToLoad = new HashSet <TBtypeEntityId>();

                        entitySyncStates.Execute(s => s.AddRequiredEntitiesToLoad(aEntitesToLoad.Add, bEntitesToLoad.Add));

                        if (aEntities == null && bEntities == null)
                        {
                            totalProgress.NotifyLoadCount(aEntitesToLoad.Count, bEntitesToLoad.Count);
                            using (totalProgress.StartARepositoryLoad())
                            {
                                aEntities = CreateDictionary(
                                    await _atypeRepository.Get(aEntitesToLoad),
                                    _atypeIdComparer);
                            }

                            using (totalProgress.StartBRepositoryLoad())
                            {
                                bEntities = CreateDictionary(
                                    await _btypeRepository.Get(bEntitesToLoad),
                                    _btypeIdComparer);
                            }
                        }

                        entitySyncStates.DoTransition(s => s.FetchRequiredEntities(aEntities, bEntities));
                        entitySyncStates.DoTransition(s => s.Resolve());

                        // since resolve may change to an new state, required entities have to be fetched again.
                        // an state is allowed only to resolve to another state, if the following states requires equal or less entities!
                        entitySyncStates.DoTransition(s => s.FetchRequiredEntities(aEntities, bEntities));

                        using (var progress = totalProgress.StartProcessing(entitySyncStates.Count))
                        {
                            await entitySyncStates.DoTransition(
                                async s =>
                            {
                                var nextState = await s.PerformSyncActionNoThrow();
                                progress.Increase();
                                return(nextState);
                            });
                        }

                        var newData = new List <IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> >();

                        entitySyncStates.Execute(s => s.AddNewRelationNoThrow(newData.Add));

                        entitySyncStates.Dispose();

                        _entityRelationDataAccess.SaveEntityRelationData(newData);
                    }
                    finally
                    {
                        _atypeRepository.Cleanup(aEntities);
                        _btypeRepository.Cleanup(bEntities);
                    }
                }
            }
            catch (Exception x)
            {
                _exceptionLogger.LogException(x, s_logger);
                return(false);
            }

            s_logger.DebugFormat("Exiting.");
            return(true);
        }