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) { var entitySyncStates = new EntitySyncStateContainer <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity>(); 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)); 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.FillIfEmpty(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); } 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); } } else { foreach (var newA in newAVersions) { var syncState = _initialSyncStateCreationStrategy.CreateFor_Added_NotExisting(newA.Key, newA.Value); syncState.AddRequiredEntitiesToLoad(aEntitesToLoad.Add, bEntitesToLoad.Add); entitySyncStates.Add(syncState); } foreach (var newB in newBVersions) { var syncState = _initialSyncStateCreationStrategy.CreateFor_NotExisting_Added(newB.Key, newB.Value); syncState.AddRequiredEntitiesToLoad(aEntitesToLoad.Add, bEntitesToLoad.Add); entitySyncStates.Add(syncState); } await entityContainer.FillIfEmpty(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 <TAtypeEntity, TAtypeEntityId, TAtypeEntityVersion>(); var bJobs = new JobList <TBtypeEntity, TBtypeEntityId, TBtypeEntityVersion>(); entitySyncStates.Execute(s => s.AddSyncronizationJob(aJobs, bJobs, logger.CreateEntitySynchronizationLogger())); 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 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); } }
public async Task SynchronizePartialNoThrow( IEnumerable <IIdWithHints <TAtypeEntityId, TAtypeEntityVersion> > aIds, IEnumerable <IIdWithHints <TBtypeEntityId, TBtypeEntityVersion> > bIds, ISynchronizationLogger logger) { s_logger.InfoFormat( "Entered partial. Syncstrategy '{0}' with Atype='{1}' and Btype='{2}'", _initialSyncStateCreationStrategy.GetType().Name, typeof(TAtypeEntity).Name, typeof(TBtypeEntity).Name); try { var knownEntityRelations = _entityRelationDataAccess.LoadEntityRelationData(); if (knownEntityRelations == null) { await SynchronizeNoThrow(logger); return; } var requestedAIdsById = aIds.ToDictionary(e => e.Id, _atypeIdComparer); var requestedBIdsById = bIds.ToDictionary(e => e.Id, _btypeIdComparer); var aIdsWithAwarenessLevel = new List <IdWithAwarenessLevel <TAtypeEntityId> >(); var bIdsWithAwarenessLevel = new List <IdWithAwarenessLevel <TBtypeEntityId> >(); using (var totalProgress = _totalProgressFactory.Create()) { var entityRelationsToUse = new List <IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> >(); var entityRelationsNotToUse = new List <IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> >(); foreach (var entityRelation in knownEntityRelations) { var isACausingSync = RemoveAFromRequestedAndCheckIfCausesSync(requestedAIdsById, entityRelation); var isBCausingSync = RemoveBFromRequestedAndCheckIfCausesSync(requestedBIdsById, entityRelation); if (isACausingSync || isBCausingSync) { aIdsWithAwarenessLevel.Add(new IdWithAwarenessLevel <TAtypeEntityId> (entityRelation.AtypeId, true)); bIdsWithAwarenessLevel.Add(new IdWithAwarenessLevel <TBtypeEntityId> (entityRelation.BtypeId, true)); entityRelationsToUse.Add(entityRelation); } else { entityRelationsNotToUse.Add(entityRelation); } } aIdsWithAwarenessLevel.AddRange(requestedAIdsById.Where(kv => !(kv.Value.WasDeletedHint ?? false)).Select(kv => new IdWithAwarenessLevel <TAtypeEntityId> (kv.Key, false))); bIdsWithAwarenessLevel.AddRange(requestedBIdsById.Where(kv => !(kv.Value.WasDeletedHint ?? false)).Select(kv => new IdWithAwarenessLevel <TBtypeEntityId> (kv.Key, false))); if (aIdsWithAwarenessLevel.Count == 0 && bIdsWithAwarenessLevel.Count == 0) { s_logger.InfoFormat("Exiting partial since there is nothing to synchronize."); return; } var synchronizationContext = await _contextFactory.Create(); Task <IReadOnlyList <EntityVersion <TAtypeEntityId, TAtypeEntityVersion> > > newAVersionsTask; if (aIdsWithAwarenessLevel.Count > 0) { newAVersionsTask = _atypeRepository.GetVersions(aIdsWithAwarenessLevel, synchronizationContext); } else { newAVersionsTask = Task.FromResult <IReadOnlyList <EntityVersion <TAtypeEntityId, TAtypeEntityVersion> > > (new EntityVersion <TAtypeEntityId, TAtypeEntityVersion>[] { }); } Task <IReadOnlyList <EntityVersion <TBtypeEntityId, TBtypeEntityVersion> > > newBVersionsTask; if (bIdsWithAwarenessLevel.Count > 0) { newBVersionsTask = _btypeRepository.GetVersions(bIdsWithAwarenessLevel, synchronizationContext); } else { newBVersionsTask = Task.FromResult <IReadOnlyList <EntityVersion <TBtypeEntityId, TBtypeEntityVersion> > > (new EntityVersion <TBtypeEntityId, TBtypeEntityVersion>[] { }); } var newAVersions = CreateDictionary( await newAVersionsTask, _atypeIdComparer); var newBVersions = CreateDictionary( await newBVersionsTask, _btypeIdComparer); using (var entityContainer = new EntityContainer(this, totalProgress, logger.ALoadEntityLogger, logger.BLoadEntityLogger)) { var newEntityRelations = await Synchronize( totalProgress, entityRelationsToUse, newAVersions, newBVersions, entityContainer, logger, synchronizationContext); entityRelationsNotToUse.AddRange(newEntityRelations); _entityRelationDataAccess.SaveEntityRelationData(entityRelationsNotToUse); } } } catch (Exception x) { logger.LogAbortedDueToError(x); _exceptionLogger.LogException(x, s_logger); } s_logger.DebugFormat("Exiting."); }
public async Task SynchronizePartialNoThrow( IEnumerable <TAtypeEntityId> aEntityIds, IEnumerable <TBtypeEntityId> bEntityIds, ISynchronizationLogger logger) { s_logger.InfoFormat("Entered. Syncstrategy '{0}' with Atype='{1}' and Btype='{2}'", _initialSyncStateCreationStrategy.GetType().Name, typeof(TAtypeEntity).Name, typeof(TBtypeEntity).Name); try { var knownEntityRelations = _entityRelationDataAccess.LoadEntityRelationData(); if (knownEntityRelations == null) { await SynchronizeNoThrow(logger); return; } var aEntitesToSynchronize = new HashSet <TAtypeEntityId> (aEntityIds, _atypeIdComparer); var bEntitesToSynchronize = new HashSet <TBtypeEntityId> (bEntityIds, _btypeIdComparer); var aIdsWithAwarenessLevel = new List <IdWithAwarenessLevel <TAtypeEntityId> >(); var bIdsWithAwarenessLevel = new List <IdWithAwarenessLevel <TBtypeEntityId> >(); using (var totalProgress = _totalProgressFactory.Create()) { var entityRelationsToUse = new List <IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> >(); var entityRelationsNotToUse = new List <IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> >(); foreach (var entityRelation in knownEntityRelations) { if (aEntitesToSynchronize.Contains(entityRelation.AtypeId) || bEntitesToSynchronize.Contains(entityRelation.BtypeId)) { aIdsWithAwarenessLevel.Add(new IdWithAwarenessLevel <TAtypeEntityId> (entityRelation.AtypeId, true)); bIdsWithAwarenessLevel.Add(new IdWithAwarenessLevel <TBtypeEntityId> (entityRelation.BtypeId, true)); aEntitesToSynchronize.Remove(entityRelation.AtypeId); bEntitesToSynchronize.Remove(entityRelation.BtypeId); entityRelationsToUse.Add(entityRelation); } else { entityRelationsNotToUse.Add(entityRelation); } } aIdsWithAwarenessLevel.AddRange(aEntitesToSynchronize.Select(id => new IdWithAwarenessLevel <TAtypeEntityId> (id, false))); bIdsWithAwarenessLevel.AddRange(bEntitesToSynchronize.Select(id => new IdWithAwarenessLevel <TBtypeEntityId> (id, false))); Task <IReadOnlyList <EntityVersion <TAtypeEntityId, TAtypeEntityVersion> > > newAVersionsTask; if (aIdsWithAwarenessLevel.Count > 0) { newAVersionsTask = _atypeRepository.GetVersions(aIdsWithAwarenessLevel); } else { newAVersionsTask = Task.FromResult <IReadOnlyList <EntityVersion <TAtypeEntityId, TAtypeEntityVersion> > > (new EntityVersion <TAtypeEntityId, TAtypeEntityVersion>[] { }); } Task <IReadOnlyList <EntityVersion <TBtypeEntityId, TBtypeEntityVersion> > > newBVersionsTask; if (bIdsWithAwarenessLevel.Count > 0) { newBVersionsTask = _btypeRepository.GetVersions(bIdsWithAwarenessLevel); } else { newBVersionsTask = Task.FromResult <IReadOnlyList <EntityVersion <TBtypeEntityId, TBtypeEntityVersion> > > (new EntityVersion <TBtypeEntityId, TBtypeEntityVersion>[] { }); } var newAVersions = CreateDictionary( await newAVersionsTask, _atypeIdComparer); var newBVersions = CreateDictionary( await newBVersionsTask, _btypeIdComparer); using (var entityContainer = new EntityContainer(this, totalProgress, logger)) { var newEntityRelations = await Synchronize( totalProgress, entityRelationsToUse, newAVersions, newBVersions, entityContainer, logger); entityRelationsNotToUse.AddRange(newEntityRelations); _entityRelationDataAccess.SaveEntityRelationData(entityRelationsNotToUse); } } } catch (Exception x) { logger.LogAbortedDueToError(x); _exceptionLogger.LogException(x, s_logger); } s_logger.DebugFormat("Exiting."); }