public async Task PerformOperations(
            IReadOnlyList <ICreateJob <TEntityId, TEntityVersion, TEntity> > createJobs,
            IReadOnlyList <IUpdateJob <TEntityId, TEntityVersion, TEntity> > updateJobs,
            IReadOnlyList <IDeleteJob <TEntityId, TEntityVersion> > deleteJobs,
            IProgressLogger progressLogger,
            TContext context)
        {
            foreach (var job in createJobs)
            {
                try
                {
                    var result = await _inner.Create(job.InitializeEntity, context);

                    job.NotifyOperationSuceeded(result);
                }
                catch (Exception x)
                {
                    if (_exceptionHandlingStrategy.DoesGracefullyAbortSynchronization(x))
                    {
                        throw;
                    }
                    else
                    {
                        job.NotifyOperationFailed(x);
                    }
                }
                progressLogger.Increase();
            }

            foreach (var job in updateJobs)
            {
                try
                {
                    var result = await _inner.TryUpdate(job.EntityId, job.Version, job.EntityToUpdate, job.UpdateEntity, context);

                    if (result != null)
                    {
                        job.NotifyOperationSuceeded(result);
                    }
                    else
                    {
                        job.NotifyEntityNotFound();
                    }
                }
                catch (Exception x)
                {
                    if (_exceptionHandlingStrategy.DoesGracefullyAbortSynchronization(x))
                    {
                        throw;
                    }
                    else
                    {
                        job.NotifyOperationFailed(x);
                    }
                }
                progressLogger.Increase();
            }

            foreach (var job in deleteJobs)
            {
                try
                {
                    if (await _inner.TryDelete(job.EntityId, job.Version, context))
                    {
                        job.NotifyOperationSuceeded();
                    }
                    else
                    {
                        job.NotifyEntityNotFound();
                    }
                }
                catch (Exception x)
                {
                    if (_exceptionHandlingStrategy.DoesGracefullyAbortSynchronization(x))
                    {
                        throw;
                    }
                    else
                    {
                        job.NotifyOperationFailed(x);
                    }
                }
                progressLogger.Increase();
            }
        }
        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);
                }
        }