private static async Task ProcessSavesAsync(
            List <EntityInfo> saveOrder,
            PersistenceManager persistenceManager,
            SaveChangesContext context,
            ISaveChangesOptions saveChangesOptions,
            ConfiguredSessionProvider sessionProvider, CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            foreach (var entityInfo in saveOrder)
            {
                await(persistenceManager.BeforeSaveEntityChangesAsync(entityInfo, context, cancellationToken)).ConfigureAwait(false);
                var beforeSaveEntityChangesTask = saveChangesOptions?.BeforeSaveEntityChangesAsync(entityInfo, context, cancellationToken);
                if (beforeSaveEntityChangesTask != null)
                {
                    await(beforeSaveEntityChangesTask).ConfigureAwait(false);
                }

                var session = sessionProvider.GetSession(entityInfo.EntityType);
                try
                {
                    switch (entityInfo.EntityState)
                    {
                    case EntityState.Modified:
                        await(session.UpdateAsync(entityInfo.Entity, cancellationToken)).ConfigureAwait(false);
                        break;

                    case EntityState.Added:
                        await(session.SaveAsync(entityInfo.Entity, cancellationToken)).ConfigureAwait(false);
                        break;

                    case EntityState.Deleted:
                        await(session.DeleteAsync(entityInfo.Entity, cancellationToken)).ConfigureAwait(false);
                        break;
                    }
                }
                catch (PropertyValueException e)
                {
                    // NH can throw this when a not null property is null or transient (e.g. not-null property references a null or transient value)
                    var errors = new[]
                    {
                        // KeyValues cannot be determined as the exception may reference another entity
                        new EntityError
                        {
                            EntityTypeName = e.EntityName,
                            ErrorMessage   = e.Message,
                            ErrorName      = "PropertyValueException",
                            PropertyName   = e.PropertyName
                        }
                    };

                    throw new EntityErrorsException(e.Message, errors);
                }
            }
        }
        internal async Task <List <KeyMapping> > FetchAndApplyChangesInternalAsync(
            PersistenceManager persistenceManager,
            SaveChangesContext context,
            ISaveChangesOptions saveChangesOptions, CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            await(persistenceManager.BeforeFetchEntitiesAsync(context, cancellationToken)).ConfigureAwait(false);
            var beforeFetchEntitiesTask = saveChangesOptions?.BeforeFetchEntitiesAsync(context, cancellationToken);

            if (beforeFetchEntitiesTask != null)
            {
                await(beforeFetchEntitiesTask).ConfigureAwait(false);
            }

            var saveMap = context.SaveMap;

            using var sessionProvider = new ConfiguredSessionProvider(_sessionProvider);
            var entitiesIdMap = new EntityIdMap(saveMap.Count);
            var saveMapList   = saveMap.ToList();

            // Make sure that entity types that have all key-many-to-one are sorted so that the associated entities will be processed before them
            saveMapList.Sort(CompareSaveMapTypes);
            foreach (var pair in saveMapList)
            {
                await(SetupDatabaseEntitiesAsync(pair.Key, pair.Value, saveMap, entitiesIdMap, sessionProvider, cancellationToken)).ConfigureAwait(false);
            }

            await(persistenceManager.BeforeApplyChangesAsync(context, cancellationToken)).ConfigureAwait(false);
            var beforeApplyChangesTask = saveChangesOptions?.BeforeApplyChangesAsync(context, cancellationToken);

            if (beforeApplyChangesTask != null)
            {
                await(beforeApplyChangesTask).ConfigureAwait(false);
            }

            AddAdditionalEntities(context.AdditionalEntities, context.SaveMap);
            context.AdditionalEntities?.Clear();
            var dependencyGraph = new DependencyGraph(saveMap.Count);

            foreach (var pair in saveMap)
            {
                await(ApplyChangesAsync(pair.Key, pair.Value, dependencyGraph, saveMap, entitiesIdMap, sessionProvider, cancellationToken)).ConfigureAwait(false);
            }

            persistenceManager.ValidateDependencyGraph(dependencyGraph, context);
            saveChangesOptions?.ValidateDependencyGraph(dependencyGraph, context);

            var saveOrder = dependencyGraph.GetSaveOrder();

            await(persistenceManager.BeforeSaveChangesAsync(saveOrder, context, cancellationToken)).ConfigureAwait(false);
            var beforeSaveChangesTask = saveChangesOptions?.BeforeSaveChangesAsync(saveOrder, context, cancellationToken);

            if (beforeSaveChangesTask != null)
            {
                await(beforeSaveChangesTask).ConfigureAwait(false);
            }

            await(ProcessSavesAsync(saveOrder, persistenceManager, context, saveChangesOptions, sessionProvider, cancellationToken)).ConfigureAwait(false);

            await(persistenceManager.AfterSaveChangesAsync(saveOrder, context, cancellationToken)).ConfigureAwait(false);
            var afterSaveChangesTask = saveChangesOptions?.AfterSaveChangesAsync(saveOrder, context, cancellationToken);

            if (afterSaveChangesTask != null)
            {
                await(afterSaveChangesTask).ConfigureAwait(false);
            }

            await(FlushSessionsAsync(sessionProvider, cancellationToken)).ConfigureAwait(false);
            await(RefreshFromSessionAsync(saveMap, sessionProvider, cancellationToken)).ConfigureAwait(false);
            var keyMappings = GetKeyMappings(saveMap).ToList();

            await(persistenceManager.AfterFlushChangesAsync(context, keyMappings, cancellationToken)).ConfigureAwait(false);
            var afterFlushChangesTask = saveChangesOptions?.AfterFlushChangesAsync(context, keyMappings, cancellationToken);

            if (afterFlushChangesTask != null)
            {
                await(afterFlushChangesTask).ConfigureAwait(false);
            }

            UpdateClientEntities(context);

            return(keyMappings);
        }