/// <summary>
        /// For each DTO that is coming from the data-source, create a clone of the associated cached POCO
        /// and store this clone in the <see cref="Thing.Revisions"/> dictionary if it is a newer revision.
        /// For older revisions, store the revision in the cached POCO's <see cref="Thing.Revisions"/> property'
        /// </summary>
        /// <param name="dtoThings">
        /// the DTO's coming from the data-source
        /// </param>
        /// <param name="uncachedOrUpdatedOrNewThingRevisions">A list that contains <see cref="CDP4Common.DTO.Thing"/>s that are </param>
        /// <remarks>
        /// If the revision of the DTO is smaller that the revision of the the cached POCO, it is a DTO that represents
        /// the state of a DTO from the past. It will be added to the cached POCO's <see cref="Thing.Revisions"/> property
        /// </remarks>
        private void UpdateThingRevisions(IEnumerable <CDP4Common.DTO.Thing> dtoThings, out IList <CDP4Common.DTO.Thing> uncachedOrUpdatedOrNewThingRevisions)
        {
            // create and store a shallow clone of the a current cached Thing
            var revisionCloneWatch = Stopwatch.StartNew();

            uncachedOrUpdatedOrNewThingRevisions = new List <CDP4Common.DTO.Thing>(dtoThings);

            foreach (var dto in dtoThings)
            {
                var cacheKey = new CacheKey(dto.Iid, dto.IterationContainerId);

                if (this.Cache.TryGetValue(cacheKey, out var currentCachedThing))
                {
                    var currentThing = currentCachedThing.Value;

                    if (dto.RevisionNumber > currentThing.RevisionNumber)
                    {
                        if (!currentThing.Revisions.ContainsKey(currentThing.RevisionNumber))
                        {
                            currentThing.Revisions.Add(currentThing.RevisionNumber, currentThing.Clone(false));
                            logger.Trace("Revision {0} added to Revisions of {1}:{2}", currentThing.RevisionNumber, currentThing.ClassKind, currentThing.Iid);
                        }
                        else
                        {
                            logger.Trace("Revision {0} of Thing {1}:{2} already exists in the Thing.Revisions cache", currentThing.RevisionNumber, currentThing.ClassKind, currentThing.Iid);
                        }

                        continue;
                    }

                    if (dto.RevisionNumber == currentThing.RevisionNumber)
                    {
                        logger.Trace("A DTO with revision {0} equal to the revision of the existing POCO {1}:{2}:{3} has been identified; The data-source has sent a revision of an object that is already present in the cache",
                                     dto.RevisionNumber, currentThing.ClassKind, currentThing.CacheKey.Thing, currentThing.CacheKey.Iteration);

                        continue;
                    }

                    if (dto.RevisionNumber < currentThing.RevisionNumber)
                    {
                        uncachedOrUpdatedOrNewThingRevisions.Remove(dto);
                        //Add the found DTO to the currentThing's Revisions property
                        var poco = dto.InstantiatePoco(this.Cache, this.IDalUri);
                        PocoThingFactory.ResolveDependencies(dto, poco);

                        if (!currentThing.Revisions.ContainsKey(dto.RevisionNumber))
                        {
                            currentThing.Revisions.Add(dto.RevisionNumber, poco);
                            logger.Trace("Revision {0} added to Revisions of {1}:{2}", dto.RevisionNumber, currentThing.ClassKind, currentThing.Iid);
                        }
                        else
                        {
                            logger.Trace("Revision {0} of Thing {1}:{2} already exists in the Thing.Revisions cache", currentThing.RevisionNumber, currentThing.ClassKind, currentThing.Iid);
                        }
                    }
                }
            }

            logger.Info("Updating Thing.Revisions took {0} [ms]", revisionCloneWatch.ElapsedMilliseconds);
        }
        /// <summary>
        /// Synchronize the Cache give an IEnumerable of DTO <see cref="Thing"/>
        /// </summary>
        /// <param name="dtoThings">
        /// the DTOs
        /// </param>
        /// <param name="activeMessageBus">
        /// An optional value indicating whether the <see cref="CDPMessageBus"/> should publish <see cref="ObjectChangedEvent"/> or not.
        /// The default value is true
        /// </param>
        /// <returns>
        /// The <see cref="Task"/> that can be awaited.
        /// </returns>
        public async Task Synchronize(IEnumerable <CDP4Common.DTO.Thing> dtoThings, bool activeMessageBus = true)
        {
            if (dtoThings == null)
            {
                throw new ArgumentNullException(nameof(dtoThings), $"The {nameof(dtoThings)} may not be null");
            }

            await this.threadLock.WaitAsync().ConfigureAwait(false);

            try
            {
                var synchronizeStopWatch = Stopwatch.StartNew();

                logger.Info("Start Synchronization of {0}", this.IDalUri);

                var existentGuid =
                    this.Cache.Select(
                        x => new Tuple <CacheKey, int>(x.Value.Value.CacheKey, x.Value.Value.RevisionNumber))
                    .ToList();

                this.CheckPartitionDependentContainmentContainerIds(dtoThings);

                this.UpdateThingRevisions(dtoThings, out var uncachedOrUpdatedOrNewerThingRevisions);

                this.thingsMarkedForDeletion = new List <Thing>();

                logger.Trace("Starting Clean-up Unused references");
                var startwatch = Stopwatch.StartNew();

                this.DtoThingToUpdate = dtoThings.ToList();

                // Add the unresolved thing to the things to resolved in case it is possible to fully resolve them with the current update
                // an example would be Citation contained by SiteDirectory where its Source is contained by a Rdl that is not loaded yet
                var unresolvedThingToUpdate = this.unresolvedDtos.Where(x => !this.DtoThingToUpdate.Select(y => y.Iid).Contains(x.Iid));
                this.DtoThingToUpdate.AddRange(unresolvedThingToUpdate);
                this.unresolvedDtos.Clear();

                if (!this.Cache.IsEmpty)
                {
                    // marks things for deletion
                    this.ComputeThingsToRemoveInUpdatedThings();
                    startwatch.Stop();
                    logger.Trace("Clean up Unused references took {0} [ms]", startwatch.ElapsedMilliseconds);
                }

                logger.Trace("Start Updating cache");
                startwatch = Stopwatch.StartNew();
                this.AddOrUpdateTheCache(uncachedOrUpdatedOrNewerThingRevisions);
                startwatch.Stop();
                logger.Trace("Updating cache took {0} [ms]", startwatch.ElapsedMilliseconds);

                logger.Trace("Start Resolving properties");
                startwatch = Stopwatch.StartNew();
                PocoThingFactory.ResolveDependencies(uncachedOrUpdatedOrNewerThingRevisions, this.Cache);
                startwatch.Stop();
                logger.Trace("Resolving properties took {0} [ms]", startwatch.ElapsedMilliseconds);

                // validate POCO's
                logger.Trace("Start validating Things");
                startwatch = Stopwatch.StartNew();
                foreach (var dtoThing in this.DtoThingToUpdate)
                {
                    var cacheKey = new CacheKey(dtoThing.Iid, dtoThing.IterationContainerId);
                    var succeed  = this.Cache.TryGetValue(cacheKey, out var updatedLazyThing);

                    if (succeed)
                    {
                        var thingObject = updatedLazyThing.Value;
                        thingObject.ValidatePoco();

                        // add to the list of unresolved dtos if there is an error
                        if (thingObject.ValidationErrors.Any())
                        {
                            this.unresolvedDtos.Add(dtoThing);
                        }
                    }
                }

                startwatch.Stop();
                logger.Trace("Validating {0} Things took {1} [ms]", this.DtoThingToUpdate.Count, startwatch.ElapsedMilliseconds);

                // message added and updated POCO's
                if (activeMessageBus)
                {
                    logger.Trace("Start Messaging");
                    startwatch = Stopwatch.StartNew();

                    var messageCounter = 0;

                    foreach (var dtoThing in uncachedOrUpdatedOrNewerThingRevisions)
                    {
                        var cacheKey = new CacheKey(dtoThing.Iid, dtoThing.IterationContainerId);
                        var succeed  = this.Cache.TryGetValue(cacheKey, out var updatedLazyThing);

                        if (succeed)
                        {
                            var thingObject = updatedLazyThing.Value;
                            var cacheId     = new CacheKey(dtoThing.Iid, dtoThing.IterationContainerId);

                            if (!existentGuid.Select(x => x.Item1).Contains(cacheId))
                            {
                                CDPMessageBus.Current.SendObjectChangeEvent(thingObject, EventKind.Added);
                                messageCounter++;
                            }
                            else
                            {
                                var cacheThingRevisionNumber = existentGuid.Single(x => x.Item1.Equals(cacheId)).Item2;

                                if (dtoThing.RevisionNumber > cacheThingRevisionNumber)
                                {
                                    // send event if revision number has increased from the old cached version
                                    CDPMessageBus.Current.SendObjectChangeEvent(thingObject, EventKind.Updated);
                                    messageCounter++;
                                }
                                else if (dtoThing.RevisionNumber < cacheThingRevisionNumber)
                                {
                                    if (this.Cache.TryGetValue(cacheId, out var cacheThing))
                                    {
                                        // send event if revision number is lower. That means that the original cached item was changed (revision was added!)                                        CDPMessageBus.Current.SendObjectChangeEvent(cacheThing.Value, EventKind.Updated);
                                        CDPMessageBus.Current.SendObjectChangeEvent(cacheThing.Value, EventKind.Updated);
                                        messageCounter++;
                                    }
                                }
                            }
                        }
                    }

                    startwatch.Stop();
                    logger.Trace("Messaging {0} Things took {1} [ms]", messageCounter, startwatch.ElapsedMilliseconds);
                }

                logger.Trace("Start Deleting things");
                startwatch = Stopwatch.StartNew();

                foreach (var markedThing in this.thingsMarkedForDeletion.Where(x => x.ChangeKind == ChangeKind.Delete))
                {
                    this.RemoveThingFromCache(markedThing);
                }

                var deletedIterationSetups = this.DtoThingToUpdate.OfType <CDP4Common.DTO.IterationSetup>().Where(x => x.IsDeleted).ToList();
                var deletedModelSetups     = this.thingsMarkedForDeletion.OfType <EngineeringModelSetup>().ToList();
                this.thingsMarkedForDeletion.Clear();

                if (deletedIterationSetups.Any())
                {
                    foreach (var deletedIterationSetup in deletedIterationSetups)
                    {
                        this.MarkAndDelete(deletedIterationSetup.IterationIid);
                    }
                }

                if (deletedModelSetups.Any())
                {
                    foreach (var deletedModelSetup in deletedModelSetups)
                    {
                        this.MarkAndDelete(deletedModelSetup.EngineeringModelIid);
                    }
                }

                startwatch.Stop();
                logger.Trace("Deleting things took {0} [ms]", startwatch.ElapsedMilliseconds);

                this.DtoThingToUpdate.Clear();

                if (this.siteDirectory == null)
                {
                    var keyvaluepair = this.Cache.Single(item => item.Value.Value.ClassKind == ClassKind.SiteDirectory);
                    this.siteDirectory = (SiteDirectory)keyvaluepair.Value.Value;
                }

                logger.Info("Finish Synchronization of {0} in {1} [ms]", this.IDalUri, synchronizeStopWatch.ElapsedMilliseconds);
            }
            catch (Exception e)
            {
                logger.Error(e);
            }
            finally
            {
                this.threadLock.Release();
                logger.Trace("Assembler thread released");
            }
        }