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); }
public LoggingEntityStateCollectionDecorator(IEntityStateCollection <WebResourceName, string> inner, ICardDavRepositoryLogger logger) { _inner = inner ?? throw new ArgumentNullException(nameof(inner)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); }
private async Task Synchronize( ITotalProgressLogger totalProgress, 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, Action <List <IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> > > saveNewRelations) { var entitySynchronizationLoggerFactory = new SynchronizationLoggerBoundEntitySynchronizationLoggerFactory <TAtypeEntity, TBtypeEntity>(logger, _fullEntitySynchronizationLoggerFactory); using (IEntityContainer <TAtypeEntityId, TAtypeEntity, TContext> aEntities = new EntityContainer <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TContext>(_atypeRepository, _atypeIdComparer, _chunkSize, _chunkedExecutor)) using (IEntityContainer <TBtypeEntityId, TBtypeEntity, TContext> bEntities = new EntityContainer <TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext>(_btypeRepository, _btypeIdComparer, _chunkSize, _chunkedExecutor)) { var entitySynchronizationContexts = await CreateEntitySyncStateContexts(aEntities, bEntities, knownEntityRelations, aStates, bStates, logger, synchronizationContext, interceptor); var totalAJobs = new JobCount(); var totalBJobs = new JobCount(); try { var chunks = _entitySyncStateChunkCreator.CreateChunks(entitySynchronizationContexts, _atypeIdComparer, _btypeIdComparer).ToArray(); totalProgress.NotifyWork(chunks.Aggregate(0, (acc, c) => acc + c.AEntitesToLoad.Count + c.BEntitesToLoad.Count), chunks.Length); foreach ((var aEntitesToLoad, var bEntitesToLoad, var currentBatch) in chunks) { var chunkLogger = totalProgress.StartChunk(); IReadOnlyDictionary <TAtypeEntityId, TAtypeEntity> aEntitiesById; using (chunkLogger.StartARepositoryLoad(aEntitesToLoad.Count)) { aEntitiesById = await aEntities.GetEntities(aEntitesToLoad, logger.ALoadEntityLogger, synchronizationContext); } IReadOnlyDictionary <TBtypeEntityId, TBtypeEntity> bEntitiesById; using (chunkLogger.StartBRepositoryLoad(bEntitesToLoad.Count)) { bEntitiesById = await bEntities.GetEntities(bEntitesToLoad, logger.BLoadEntityLogger, synchronizationContext); } currentBatch.ForEach(s => s.FetchRequiredEntities(aEntitiesById, bEntitiesById)); currentBatch.ForEach(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! currentBatch.ForEach(s => s.FetchRequiredEntities(aEntitiesById, bEntitiesById)); var aJobs = new JobList <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity>(); var bJobs = new JobList <TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity>(); currentBatch.ForEach(s => s.AddSyncronizationJob(aJobs, bJobs, entitySynchronizationLoggerFactory, synchronizationContext)); totalAJobs = totalAJobs.Add(aJobs.Count); totalBJobs = totalBJobs.Add(bJobs.Count); try { using (var progress = chunkLogger.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); } currentBatch.ForEach(s => s.NotifyJobExecuted()); } catch (Exception x) { if (_exceptionHandlingStrategy.DoesGracefullyAbortSynchronization(x)) { entitySynchronizationContexts.ForEach(s => s.Abort()); SaveNewRelations(entitySynchronizationContexts, saveNewRelations); } throw; } } } finally { s_logger.InfoFormat($"A repository jobs: {totalAJobs}"); s_logger.InfoFormat($"B repository jobs: {totalBJobs}"); logger.LogJobs(totalAJobs.ToString(), totalBJobs.ToString()); } SaveNewRelations(entitySynchronizationContexts, saveNewRelations); } }