private IEntitySyncState <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity> CreateInitialSyncState(
            IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> knownEntityRelation,
            bool newAVersionAvailable,
            TAtypeEntityVersion newAVersion,
            bool newBVersionAvailable,
            TBtypeEntityVersion newBVersion,
            VersionDeltaLoginInformation aLogInfo,
            VersionDeltaLoginInformation bLogInfo)
        {
            IEntitySyncState <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity> state;

            if (newAVersionAvailable)
            {
                var aChanged = !_atypeVersionComparer.Equals(newAVersion, knownEntityRelation.AtypeVersion);
                if (aChanged)
                {
                    aLogInfo.IncChanged();
                    if (newBVersionAvailable)
                    {
                        var bChanged = !_btypeVersionComparer.Equals(newBVersion, knownEntityRelation.BtypeVersion);
                        if (bChanged)
                        {
                            bLogInfo.IncChanged();
                            state = _initialSyncStateCreationStrategy.CreateFor_Changed_Changed(knownEntityRelation, newAVersion, newBVersion);
                        }
                        else
                        {
                            bLogInfo.IncUnchanged();
                            state = _initialSyncStateCreationStrategy.CreateFor_Changed_Unchanged(knownEntityRelation, newAVersion);
                        }
                    }
                    else
                    {
                        bLogInfo.IncDeleted();
                        state = _initialSyncStateCreationStrategy.CreateFor_Changed_Deleted(knownEntityRelation, newAVersion);
                    }
                }
                else
                {
                    aLogInfo.IncUnchanged();
                    if (newBVersionAvailable)
                    {
                        var bChanged = !_btypeVersionComparer.Equals(newBVersion, knownEntityRelation.BtypeVersion);
                        if (bChanged)
                        {
                            bLogInfo.IncChanged();
                            state = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Changed(knownEntityRelation, newBVersion);
                        }
                        else
                        {
                            bLogInfo.IncUnchanged();
                            state = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Unchanged(knownEntityRelation);
                        }
                    }
                    else
                    {
                        bLogInfo.IncDeleted();
                        state = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Deleted(knownEntityRelation);
                    }
                }
            }
            else
            {
                aLogInfo.IncDeleted();
                if (newBVersionAvailable)
                {
                    var bChanged = !_btypeVersionComparer.Equals(newBVersion, knownEntityRelation.BtypeVersion);
                    if (bChanged)
                    {
                        bLogInfo.IncChanged();
                        state = _initialSyncStateCreationStrategy.CreateFor_Deleted_Changed(knownEntityRelation, newBVersion);
                    }
                    else
                    {
                        bLogInfo.IncUnchanged();
                        state = _initialSyncStateCreationStrategy.CreateFor_Deleted_Unchanged(knownEntityRelation);
                    }
                }
                else
                {
                    bLogInfo.IncDeleted();
                    state = _initialSyncStateCreationStrategy.CreateFor_Deleted_Deleted(knownEntityRelation);
                }
            }
            return(state);
        }
Пример #2
0
        private async Task <List <IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> > > Synchronize(
            ITotalProgressLogger totalProgress,
            IEnumerable <IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> > knownEntityRelations,
            Dictionary <TAtypeEntityId, TAtypeEntityVersion> newAVersions,
            Dictionary <TBtypeEntityId, TBtypeEntityVersion> newBVersions,
            EntityContainer entityContainer,
            ISynchronizationLogger logger,
            TContext synchronizationContext,
            ISynchronizationInterceptor <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext> interceptor)
        {
            var entitySyncStates = new EntitySyncStateContainer <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext>();

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

            foreach (var knownEntityRelationData in knownEntityRelations)
            {
                TAtypeEntityVersion newAVersion;
                TBtypeEntityVersion newBVersion;

                var newAVersionAvailable = newAVersions.TryGetValue(knownEntityRelationData.AtypeId, out newAVersion);
                var newBVersionAvailable = newBVersions.TryGetValue(knownEntityRelationData.BtypeId, out newBVersion);

                if (newAVersionAvailable)
                {
                    newAVersions.Remove(knownEntityRelationData.AtypeId);
                }

                if (newBVersionAvailable)
                {
                    newBVersions.Remove(knownEntityRelationData.BtypeId);
                }

                var entitySyncState = CreateInitialSyncState(knownEntityRelationData, newAVersionAvailable, newAVersion, newBVersionAvailable, newBVersion, aDeltaLogInfo, bDeltaLogInfo);

                entitySyncStates.Add(entitySyncState);
            }

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

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

            await _atypeRepository.VerifyUnknownEntities(newAVersions, synchronizationContext);

            await _btypeRepository.VerifyUnknownEntities(newBVersions, synchronizationContext);

            if (newAVersions.Count > 0 && newBVersions.Count > 0)
            {
                foreach (var newA in newAVersions)
                {
                    aEntitesToLoad.Add(newA.Key);
                }

                foreach (var newB in newBVersions)
                {
                    bEntitesToLoad.Add(newB.Key);
                }

                await entityContainer.EnsureEntitiesLoaded(aEntitesToLoad, bEntitesToLoad, synchronizationContext);

                var newAtypeEntities = GetSubSet(entityContainer.AEntities, newAVersions.Keys, _atypeIdComparer);
                var newBtypeEntities = GetSubSet(entityContainer.BEntities, newBVersions.Keys, _btypeIdComparer);

                var matchingEntites = _initialEntityMatcher.FindMatchingEntities(
                    _entityRelationDataFactory,
                    newAtypeEntities,
                    newBtypeEntities,
                    newAVersions,
                    newBVersions);

                foreach (var knownEntityRelationData in matchingEntites)
                {
                    newAVersions.Remove(knownEntityRelationData.AtypeId);
                    newBVersions.Remove(knownEntityRelationData.BtypeId);
                    var entitySyncState = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Unchanged(knownEntityRelationData);
                    entitySyncStates.Add(entitySyncState);
                    aDeltaLogInfo.IncUnchanged();
                    bDeltaLogInfo.IncUnchanged();
                }
            }

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

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

            entitySyncStates.TransformStates(s => interceptor.TransformInitialCreatedStates(s, _syncStateFactory));
            entitySyncStates.Execute(s => s.AddRequiredEntitiesToLoad(aEntitesToLoad.Add, bEntitesToLoad.Add));

            await entityContainer.EnsureEntitiesLoaded(aEntitesToLoad, bEntitesToLoad, synchronizationContext);


            // all the leftovers in newAVersions and newBVersions must be the added ones
            aDeltaLogInfo.IncAdded(newAVersions.Count);
            bDeltaLogInfo.IncAdded(newBVersions.Count);
            s_logger.InfoFormat("Atype delta: {0}", aDeltaLogInfo);
            s_logger.InfoFormat("Btype delta: {0}", bDeltaLogInfo);
            logger.LogDeltas(aDeltaLogInfo, bDeltaLogInfo);

            entitySyncStates.DoTransition(s => s.FetchRequiredEntities(entityContainer.AEntities, entityContainer.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(entityContainer.AEntities, entityContainer.BEntities));

            var aJobs = new JobList <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity> ();
            var bJobs = new JobList <TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity> ();

            entitySyncStates.Execute(s => s.AddSyncronizationJob(aJobs, bJobs, logger.CreateEntitySynchronizationLogger(), synchronizationContext));

            s_logger.InfoFormat($"A repository jobs: Create {aJobs.CreateJobs.Count} , Update {aJobs.UpdateJobs.Count} , Delete {aJobs.DeleteJobs.Count}");
            s_logger.InfoFormat($"B repository jobs: Create {bJobs.CreateJobs.Count} , Update {bJobs.UpdateJobs.Count} , Delete {bJobs.DeleteJobs.Count}");

            using (var progress = totalProgress.StartProcessing(aJobs.TotalJobCount + bJobs.TotalJobCount))
            {
                await _atypeWriteRepository.PerformOperations(aJobs.CreateJobs, aJobs.UpdateJobs, aJobs.DeleteJobs, progress, synchronizationContext);

                await _btypeWriteRepository.PerformOperations(bJobs.CreateJobs, bJobs.UpdateJobs, bJobs.DeleteJobs, progress, synchronizationContext);
            }

            entitySyncStates.DoTransition(s => s.NotifyJobExecuted());

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

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

            entitySyncStates.Dispose();


            return(newEntityRelations);
        }
        private async Task <IReadOnlyList <IEntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext> > > CreateEntitySyncStateContexts(
            IEntityContainer <TAtypeEntityId, TAtypeEntity, TContext> aEntities,
            IEntityContainer <TBtypeEntityId, TBtypeEntity, TContext> bEntities,
            IEnumerable <IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> > knownEntityRelations,
            IEntityStateCollection <TAtypeEntityId, TAtypeEntityVersion> aStates,
            IEntityStateCollection <TBtypeEntityId, TBtypeEntityVersion> bStates,
            ISynchronizationLogger logger,
            TContext synchronizationContext,
            ISynchronizationInterceptor <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext> interceptor)
        {
            var entitySynchronizationContexts = new List <IEntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext> >();

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

            foreach (var knownEntityRelationData in knownEntityRelations)
            {
                (var aState, var aRepositoryVersion) = aStates.RemoveState(knownEntityRelationData.AtypeId, knownEntityRelationData.AtypeVersion);
                (var bState, var bRepositoryVersion) = bStates.RemoveState(knownEntityRelationData.BtypeId, knownEntityRelationData.BtypeVersion);

                entitySynchronizationContexts.Add(new EntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext>(
                                                      CreateInitialSyncState(knownEntityRelationData, aState, bState, aRepositoryVersion, bRepositoryVersion, aDeltaLogInfo, bDeltaLogInfo)));
            }

            var newAVersions = aStates.DisposeAndGetLeftovers();
            var newBVersions = bStates.DisposeAndGetLeftovers();

            await _atypeRepository.VerifyUnknownEntities(newAVersions, synchronizationContext);

            await _btypeRepository.VerifyUnknownEntities(newBVersions, synchronizationContext);

            if (newAVersions.Count > 0 && newBVersions.Count > 0)
            {
                s_logger.Info($"Performing entity matching with {newAVersions.Count} Atype and {newBVersions.Count} Btype entities.");

                var matchingEntites = _initialEntityMatcher.FindMatchingEntities(
                    _entityRelationDataFactory,
                    await aEntities.GetTransformedEntities(newAVersions.Keys, logger.ALoadEntityLogger, synchronizationContext, e => _aMatchDataFactory.CreateMatchData(e.Entity)),
                    await bEntities.GetTransformedEntities(newBVersions.Keys, logger.BLoadEntityLogger, synchronizationContext, e => _bMatchDataFactory.CreateMatchData(e.Entity)),
                    newAVersions,
                    newBVersions);

                foreach (var knownEntityRelationData in matchingEntites)
                {
                    newAVersions.Remove(knownEntityRelationData.AtypeId);
                    newBVersions.Remove(knownEntityRelationData.BtypeId);
                    var entitySyncState = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Unchanged(knownEntityRelationData);
                    entitySynchronizationContexts.Add(new EntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext>(entitySyncState));
                    aDeltaLogInfo.IncUnchanged();
                    bDeltaLogInfo.IncUnchanged();
                }

                s_logger.Info("Entity matching finished.");
            }

            foreach (var newA in newAVersions)
            {
                var syncState = _initialSyncStateCreationStrategy.CreateFor_Added_NotExisting(newA.Key, newA.Value);
                entitySynchronizationContexts.Add(new EntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext>(syncState));
            }

            foreach (var newB in newBVersions)
            {
                var syncState = _initialSyncStateCreationStrategy.CreateFor_NotExisting_Added(newB.Key, newB.Value);
                entitySynchronizationContexts.Add(new EntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext>(syncState));
            }

            interceptor.TransformInitialCreatedStates(entitySynchronizationContexts, _syncStateFactory);

            // all the leftovers in newAVersions and newBVersions must be the added ones
            aDeltaLogInfo.IncAdded(newAVersions.Count);
            bDeltaLogInfo.IncAdded(newBVersions.Count);
            s_logger.InfoFormat("Atype delta: {0}", aDeltaLogInfo);
            s_logger.InfoFormat("Btype delta: {0}", bDeltaLogInfo);
            logger.LogDeltas(aDeltaLogInfo, bDeltaLogInfo);

            return(entitySynchronizationContexts);
        }
        private IEntitySyncState <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext> CreateInitialSyncState(
            IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> knownEntityRelation,
            EntityState aState,
            EntityState bState,
            TAtypeEntityVersion aRepositoryVersion,
            TBtypeEntityVersion bRepositoryVersion,
            VersionDeltaLoginInformation aLogInfo,
            VersionDeltaLoginInformation bLogInfo)
        {
            IEntitySyncState <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext> state;

            switch (aState)
            {
            case EntityState.ChangedOrAdded:
                aLogInfo.IncChanged();
                switch (bState)
                {
                case EntityState.ChangedOrAdded:
                    bLogInfo.IncChanged();
                    state = _initialSyncStateCreationStrategy.CreateFor_Changed_Changed(knownEntityRelation, aRepositoryVersion, bRepositoryVersion);
                    break;

                case EntityState.Unchanged:
                    bLogInfo.IncUnchanged();
                    state = _initialSyncStateCreationStrategy.CreateFor_Changed_Unchanged(knownEntityRelation, aRepositoryVersion);
                    break;

                case EntityState.Deleted:
                    bLogInfo.IncDeleted();
                    state = _initialSyncStateCreationStrategy.CreateFor_Changed_Deleted(knownEntityRelation, aRepositoryVersion);
                    break;

                default:
                    throw new NotImplementedException($"'{bState}' not implemented");
                }
                break;

            case EntityState.Unchanged:
                aLogInfo.IncUnchanged();
                switch (bState)
                {
                case EntityState.ChangedOrAdded:
                    bLogInfo.IncChanged();
                    state = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Changed(knownEntityRelation, bRepositoryVersion);
                    break;

                case EntityState.Unchanged:
                    bLogInfo.IncUnchanged();
                    state = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Unchanged(knownEntityRelation);
                    break;

                case EntityState.Deleted:
                    bLogInfo.IncDeleted();
                    state = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Deleted(knownEntityRelation);
                    break;

                default:
                    throw new NotImplementedException($"'{bState}' not implemented");
                }
                break;

            case EntityState.Deleted:
                aLogInfo.IncDeleted();
                switch (bState)
                {
                case EntityState.ChangedOrAdded:
                    bLogInfo.IncChanged();
                    state = _initialSyncStateCreationStrategy.CreateFor_Deleted_Changed(knownEntityRelation, bRepositoryVersion);
                    break;

                case EntityState.Unchanged:
                    bLogInfo.IncUnchanged();
                    state = _initialSyncStateCreationStrategy.CreateFor_Deleted_Unchanged(knownEntityRelation);
                    break;

                case EntityState.Deleted:
                    bLogInfo.IncDeleted();
                    state = _initialSyncStateCreationStrategy.CreateFor_Deleted_Deleted(knownEntityRelation);
                    break;

                default:
                    throw new NotImplementedException($"'{bState}' not implemented");
                }
                break;

            default:
                throw new NotImplementedException($"'{aState}' not implemented");
            }

            return(state);
        }
        private IEntitySyncState <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity> CreateInitialSyncState(
            IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> cachedData,
            bool repositoryAVersionAvailable,
            TAtypeEntityVersion repositoryAVersion,
            bool repositoryBVersionAvailable,
            TBtypeEntityVersion repositoryBVersion,
            VersionDeltaLoginInformation aLogInfo,
            VersionDeltaLoginInformation bLogInfo)
        {
            IEntitySyncState <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity> state;

            if (repositoryAVersionAvailable)
            {
                var aChanged = !_atypeVersionComparer.Equals(repositoryAVersion, cachedData.AtypeVersion);
                if (aChanged)
                {
                    aLogInfo.IncChanged();
                    if (repositoryBVersionAvailable)
                    {
                        var bChanged = !_btypeVersionComparer.Equals(repositoryBVersion, cachedData.BtypeVersion);
                        if (bChanged)
                        {
                            bLogInfo.IncChanged();
                            state = _initialSyncStateCreationStrategy.CreateFor_Changed_Changed(cachedData, repositoryAVersion, repositoryBVersion);
                        }
                        else
                        {
                            bLogInfo.IncUnchanged();
                            state = _initialSyncStateCreationStrategy.CreateFor_Changed_Unchanged(cachedData, repositoryAVersion);
                        }
                    }
                    else
                    {
                        bLogInfo.IncDeleted();
                        state = _initialSyncStateCreationStrategy.CreateFor_Changed_Deleted(cachedData, repositoryAVersion);
                    }
                }
                else
                {
                    aLogInfo.IncUnchanged();
                    if (repositoryBVersionAvailable)
                    {
                        var bChanged = !_btypeVersionComparer.Equals(repositoryBVersion, cachedData.BtypeVersion);
                        if (bChanged)
                        {
                            bLogInfo.IncChanged();
                            state = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Changed(cachedData, repositoryBVersion);
                        }
                        else
                        {
                            bLogInfo.IncUnchanged();
                            state = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Unchanged(cachedData);
                        }
                    }
                    else
                    {
                        bLogInfo.IncDeleted();
                        state = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Deleted(cachedData);
                    }
                }
            }
            else
            {
                aLogInfo.IncDeleted();
                if (repositoryBVersionAvailable)
                {
                    var bChanged = !_btypeVersionComparer.Equals(repositoryBVersion, cachedData.BtypeVersion);
                    if (bChanged)
                    {
                        bLogInfo.IncChanged();
                        state = _initialSyncStateCreationStrategy.CreateFor_Deleted_Changed(cachedData, repositoryBVersion);
                    }
                    else
                    {
                        bLogInfo.IncUnchanged();
                        state = _initialSyncStateCreationStrategy.CreateFor_Deleted_Unchanged(cachedData);
                    }
                }
                else
                {
                    bLogInfo.IncDeleted();
                    state = _initialSyncStateCreationStrategy.CreateFor_Deleted_Deleted(cachedData);
                }
            }
            return(state);
        }