async Task BackupAsync()
        {
            this.events.StartingBackup();
            Guid   backupId          = Guid.NewGuid();
            string dbBackupDirectory = Path.Combine(this.backupPath, backupId.ToString());

            BackupMetadata     newBackupMetadata  = new BackupMetadata(backupId, this.dataBackupRestore.DataBackupFormat, DateTime.UtcNow, this.dbStores.Keys.ToList());
            BackupMetadataList backupMetadataList = new BackupMetadataList(new List <BackupMetadata> {
                newBackupMetadata
            });

            try
            {
                Directory.CreateDirectory(dbBackupDirectory);

                // Backup other stores.
                foreach (string store in this.dbStores.Keys)
                {
                    IDbStore dbStore = this.dbStoreProvider.GetDbStore(store);
                    await this.DbStoreBackupAsync(store, dbStore, dbBackupDirectory);
                }

                using (StreamWriter file = File.CreateText(Path.Combine(this.backupPath, BackupMetadataFileName)))
                {
                    JsonSerializer serializer = new JsonSerializer();
                    serializer.Serialize(file, backupMetadataList);
                }

                this.events.BackupComplete();

                // Clean any old backups.
                this.CleanupUnknownBackups(this.backupPath, backupMetadataList);
            }
            catch (Exception exception)
            {
                this.events.BackupFailure($"The backup operation failed with error ${exception}.");

                // Clean up any artifacts of the attempted backup.
                this.CleanupKnownBackups(this.backupPath, backupMetadataList);
            }
        }
        void CleanupKnownBackups(string backupPath, BackupMetadataList metadataList)
        {
            DirectoryInfo    backupDirInfo       = new DirectoryInfo(backupPath);
            HashSet <string> knownBackupDirNames = new HashSet <string>(metadataList.Backups.Select(x => x.Id.ToString()), StringComparer.OrdinalIgnoreCase);

            foreach (DirectoryInfo dir in backupDirInfo.GetDirectories())
            {
                if (knownBackupDirNames.Contains(dir.Name))
                {
                    try
                    {
                        dir.Delete(true);
                    }
                    catch (Exception ex)
                    {
                        this.events.DeletionError(dir.FullName, ex);
                    }
                }
            }

            this.events.BackupArtifactsCleanedUp();
        }
        async Task RestoreAsync()
        {
            string backupMetadataFilePath = Path.Combine(this.backupPath, BackupMetadataFileName);

            if (!File.Exists(backupMetadataFilePath))
            {
                this.events.NoBackupsForRestore();
                return;
            }

            try
            {
                string             fileText           = File.ReadAllText(backupMetadataFilePath);
                BackupMetadataList backupMetadataList = JsonConvert.DeserializeObject <BackupMetadataList>(fileText);
                BackupMetadata     backupMetadata     = backupMetadataList.Backups[0];
                this.events.BackupInformation(backupMetadata.Id, backupMetadata.SerializationFormat, backupMetadata.TimestampUtc, backupMetadata.Stores);

                string latestBackupDirPath = Path.Combine(this.backupPath, backupMetadata.Id.ToString());
                if (Directory.Exists(latestBackupDirPath))
                {
                    this.events.RestoringFromBackup(backupMetadata.Id);
                    foreach (string store in backupMetadata.Stores)
                    {
                        IDbStore dbStore;
                        if (!store.Equals(DefaultStoreBackupName, StringComparison.OrdinalIgnoreCase))
                        {
                            dbStore = this.GetDbStore(store);
                        }
                        else
                        {
                            dbStore = this.GetDbStore();
                        }

                        await this.DbStoreRestoreAsync(store, dbStore, latestBackupDirPath);
                    }

                    this.events.RestoreComplete();
                }
                else
                {
                    this.events.NoBackupsForRestore();
                }
            }
            catch (Exception exception)
            {
                this.events.RestoreFailure($"The restore operation failed with error ${exception}.");

                // Clean up any restored state in the dictionary if the backup fails midway.
                foreach (string store in this.dbStores.Keys.ToList())
                {
                    this.RemoveDbStore(store);
                }

                this.RemoveDbStore();
            }
            finally
            {
                // Delete all other backups as we've either:
                // 1. Restored successfully.
                // 2. Failed during restore which would indicate a bad backup.
                this.CleanupAllBackups(this.backupPath);
            }
        }