Skip to content

Instantly share code, notes, and snippets.

@PureKrome
Created September 20, 2018 09:30
Show Gist options
  • Save PureKrome/ba180e59116b88dfbc06d207ca24bbb8 to your computer and use it in GitHub Desktop.
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
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