Created
September 20, 2018 09:30
-
-
Save PureKrome/ba180e59116b88dfbc06d207ca24bbb8 to your computer and use it in GitHub Desktop.
RavenDb DocumentStore extensions class for simplifying the setup of RavenDb in a .NET Core app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Extensions.Logging; | |
using Polly; | |
using Raven.Client.Documents; | |
using Raven.Client.Documents.Indexes; | |
using Raven.Client.Documents.Operations; | |
using Raven.Client.Exceptions.Database; | |
using Raven.Client.Exceptions.Security; | |
using Raven.Client.ServerWide; | |
using Raven.Client.ServerWide.Operations; | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
namespace Hornet.Core.RavenDb | |
{ | |
public static class DocumentStoreExtensions | |
{ | |
// NOTE: Ignore DatabaseDoesNotExistException or AuthorizationException exceptions. | |
private static Policy CheckRavenDbPolicy(ILogger logger) | |
{ | |
return Policy | |
.Handle<Exception>(exception => !(exception is DatabaseDoesNotExistException) && | |
!(exception is AuthorizationException)) | |
.WaitAndRetry(5, count => | |
{ | |
return TimeSpan.FromSeconds(count * 5); // 5, 10, 15, 20, 25 seconds wait periods. | |
}, (exception, timeSpan, __) => | |
{ | |
logger.LogWarning($"Failed to connect to RavenDb. It may not be ready. Retrying ... time to wait: {timeSpan}. Exception: {exception.GetType().ToString()} {exception.Message}"); | |
}); | |
} | |
/// <summary> | |
/// Setups a RavenDb database, ready to be used with fake data (if provided) and any indexes (if provided). | |
/// </summary> | |
/// <param name="documentStore">DocumentStore to check.</param> | |
/// <param name="logger">Logger.</param> | |
/// <param name="documentCollections">A collection of Document-Collections, where each collection are any documents to be stored.</param> | |
/// <param name="indexAssembly">Assembly which contains any indexes that need to be created/updated.</param> | |
/// <param name="policy">Polly policy to check if the DB is up and running.</param> | |
public static void SetupRavenDb(this IDocumentStore documentStore, | |
ILogger logger, | |
IEnumerable<IEnumerable> documentCollections = null, | |
Type indexAssembly = null, | |
Policy policy = null) | |
{ | |
if (documentStore == null) | |
{ | |
throw new ArgumentNullException(nameof(documentStore)); | |
} | |
if (logger == null) | |
{ | |
throw new ArgumentNullException(nameof(logger)); | |
} | |
logger.LogInformation($"RavenDb -> checking if the database '{documentStore.Database}' exists and if any data exists..."); | |
DatabaseStatistics existingDatabaseStatistics = null; | |
try | |
{ | |
var checkRavenDbPolicy = policy ?? CheckRavenDbPolicy(logger); | |
checkRavenDbPolicy.Execute(() => | |
{ | |
existingDatabaseStatistics = documentStore.Maintenance.Send(new GetStatisticsOperation()); | |
}); | |
} | |
catch (DatabaseDoesNotExistException) | |
{ | |
existingDatabaseStatistics = null; // No statistics because there's no Db tenant. | |
} | |
catch (AuthorizationException) | |
{ | |
// We failed to authenticate against the database. This _usually_ means that we | |
// probably didn't have ADMIN rights against a db (so we can't do high level stuff) | |
// but we could still do other stuff, like add fake data for seeding the DB. | |
// SUMMARY: Db Tenant might exist, so lets assume that it does (safer bet). | |
existingDatabaseStatistics = new DatabaseStatistics(); | |
} | |
SetupDatabaseTenant(documentStore, | |
existingDatabaseStatistics != null, | |
logger); | |
SetupDocumentCollections(documentStore, | |
existingDatabaseStatistics?.CountOfDocuments ?? 0, | |
documentCollections, | |
logger); | |
SetupIndexes(documentStore, | |
indexAssembly, | |
logger); | |
logger.LogInformation(" - All finished setting up the database (⌐■_■)"); | |
} | |
private static void SetupDatabaseTenant(IDocumentStore documentStore, | |
bool doesDatabaseExist, | |
ILogger logger) | |
{ | |
if (documentStore == null) | |
{ | |
throw new ArgumentNullException(nameof(documentStore)); | |
} | |
if (logger == null) | |
{ | |
throw new ArgumentNullException(nameof(logger)); | |
} | |
if (doesDatabaseExist) | |
{ | |
logger.LogDebug(" - Database exists: no need to create one."); | |
return; | |
} | |
// Create the db if it doesn't exist. | |
// This will mainly occur for in memory localhost development. | |
logger.LogDebug(" - ** No database tenant found so creating a new database tenant ...."); | |
var databaseRecord = new DatabaseRecord(documentStore.Database); | |
var createDbOperation = new CreateDatabaseOperation(databaseRecord); | |
documentStore.Maintenance.Server.Send(createDbOperation); | |
} | |
private static void SetupDocumentCollections(IDocumentStore documentStore, | |
long documentCount, | |
IEnumerable<IEnumerable> documentCollections, | |
ILogger logger) | |
{ | |
if (documentStore == null) | |
{ | |
throw new ArgumentNullException(nameof(documentStore)); | |
} | |
if (logger == null) | |
{ | |
throw new ArgumentNullException(nameof(logger)); | |
} | |
if (documentCount > 0) | |
{ | |
logger.LogDebug($" - Skipping seeding fake data because database has {documentCount:N0} documents already in it."); | |
return; | |
} | |
if (documentCollections == null) | |
{ | |
logger.LogDebug(" - Skipping seeding fake data because no fake data was provided."); | |
return; | |
} | |
int documentCollectionCount = 0, documentsCount = 0; | |
logger.LogDebug(" - Seeding fake data ...."); | |
using (var session = documentStore.OpenSession()) | |
{ | |
foreach (var documentCollection in documentCollections) | |
{ | |
documentCollectionCount++; | |
foreach (var document in documentCollection) | |
{ | |
documentsCount++; | |
session.Store(document); | |
} | |
} | |
session.SaveChanges(); | |
} | |
logger.LogDebug($" - Finished. Found {documentCollectionCount} document collections and stored {documentsCount} documents."); | |
} | |
private static void SetupIndexes(IDocumentStore documentStore, | |
Type indexAssembly, | |
ILogger logger) | |
{ | |
if (documentStore == null) | |
{ | |
throw new ArgumentNullException(nameof(documentStore)); | |
} | |
if (logger == null) | |
{ | |
throw new ArgumentNullException(nameof(logger)); | |
} | |
if (indexAssembly != null) | |
{ | |
logger.LogDebug(" - Creating indexes (only on Development/localhost) ...."); | |
IndexCreation.CreateIndexes(indexAssembly.Assembly, | |
documentStore); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment