// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Aspire;
using Aspire.MongoDB.Driver;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using MongoDB.Driver.Core.Configuration;
using MongoDB.Driver.Core.Extensions.DiagnosticSources;
namespace Microsoft.Extensions.Hosting;
///
/// Extension methods for connecting MongoDB database with MongoDB.Driver client.
///
public static class AspireMongoDBDriverExtensions
{
private const string DefaultConfigSectionName = "Aspire:MongoDB:Driver";
private const string ActivityNameSource = "MongoDB.Driver.Core.Extensions.DiagnosticSources";
///
/// Registers and instances for connecting MongoDB database with MongoDB.Driver client.
///
/// The to read config from and add services to.
/// A name used to retrieve the connection string from the ConnectionStrings configuration section.
/// An optional delegate that can be used for customizing options. It's invoked after the settings are read from the configuration.
/// Reads the configuration from "Aspire:MongoDB:Driver" section.
/// An optional delegate that can be used for customizing MongoClientSettings.
/// Thrown when mandatory is not provided.
public static void AddMongoDBClient(
this IHostApplicationBuilder builder,
string connectionName,
Action? configureSettings = null,
Action? configureClientSettings = null)
{
AddMongoDBClient(builder, configureSettings, configureClientSettings, connectionName, serviceKey: null);
}
///
/// Registers and instances for connecting MongoDB database with MongoDB.Driver client.
///
/// The to read config from and add services to.
/// The name of the component, which is used as the of the service and also to retrieve the connection string from the ConnectionStrings configuration section.
/// An optional delegate that can be used for customizing options. It's invoked after the settings are read from the configuration.
/// Reads the configuration from "Aspire:MongoDB:Driver:{name}" section.
/// An optional delegate that can be used for customizing MongoClientSettings.
/// Thrown if mandatory is null.
/// Thrown when mandatory is not provided.
public static void AddKeyedMongoDBClient(
this IHostApplicationBuilder builder,
string name,
Action? configureSettings = null,
Action? configureClientSettings = null)
{
ArgumentException.ThrowIfNullOrEmpty(name);
AddMongoDBClient(
builder,
configureSettings,
configureClientSettings,
connectionName: name,
serviceKey: name);
}
private static void AddMongoDBClient(
this IHostApplicationBuilder builder,
Action? configureSettings,
Action? configureClientSettings,
string connectionName,
object? serviceKey)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentException.ThrowIfNullOrEmpty(connectionName);
var settings = builder.GetMongoDBSettings(
connectionName,
configureSettings);
builder.AddMongoClient(
settings,
connectionName,
configureClientSettings,
serviceKey);
if (!settings.DisableTracing)
{
builder.Services
.AddOpenTelemetry()
.WithTracing(tracer => tracer.AddSource(ActivityNameSource));
}
builder.AddMongoDatabase(settings.ConnectionString, serviceKey);
builder.AddHealthCheck(
connectionName,
serviceKey,
settings);
}
private static void AddMongoClient(
this IHostApplicationBuilder builder,
MongoDBSettings mongoDbSettings,
string connectionName,
Action? configureClientSettings,
object? serviceKey)
{
if (serviceKey is null)
{
builder
.Services
.AddSingleton(sp => sp.CreateMongoClient(connectionName, mongoDbSettings, configureClientSettings));
return;
}
builder
.Services
.AddKeyedSingleton(serviceKey, (sp, _) => sp.CreateMongoClient(connectionName, mongoDbSettings, configureClientSettings));
}
private static void AddMongoDatabase(
this IHostApplicationBuilder builder,
string? connectionString,
object? serviceKey = null)
{
if (string.IsNullOrWhiteSpace(connectionString))
{
return;
}
var mongoUrl = MongoUrl.Create(connectionString);
if (string.IsNullOrWhiteSpace(mongoUrl.DatabaseName))
{
return;
}
if (serviceKey is null)
{
builder.Services.AddSingleton(provider =>
{
return provider
.GetRequiredService()
.GetDatabase(mongoUrl.DatabaseName);
});
return;
}
builder.Services.AddKeyedSingleton(serviceKey, (provider, _) =>
{
return provider
.GetRequiredKeyedService(serviceKey)
.GetDatabase(mongoUrl.DatabaseName);
});
}
private static void AddHealthCheck(
this IHostApplicationBuilder builder,
string connectionName,
object? serviceKey,
MongoDBSettings settings)
{
if (settings.DisableHealthChecks || string.IsNullOrWhiteSpace(settings.ConnectionString))
{
return;
}
var healthCheckName = serviceKey is null ? "MongoDB.Driver" : $"MongoDB.Driver_{connectionName}";
builder.TryAddHealthCheck(
healthCheckName,
healthCheck => healthCheck.AddMongoDb(
serviceKey is null
? sp => sp.GetRequiredService()
: sp => sp.GetRequiredKeyedService(serviceKey),
_ => MongoUrl.Create(settings.ConnectionString).DatabaseName,
healthCheckName,
null,
null,
settings.HealthCheckTimeout > 0 ? TimeSpan.FromMilliseconds(settings.HealthCheckTimeout.Value) : null));
}
private static MongoClient CreateMongoClient(
this IServiceProvider serviceProvider,
string connectionName,
MongoDBSettings mongoDbSettings,
Action? configureClientSettings)
{
mongoDbSettings.ValidateSettings(connectionName);
var clientSettings = MongoClientSettings.FromConnectionString(mongoDbSettings.ConnectionString);
if (!mongoDbSettings.DisableTracing)
{
clientSettings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber());
}
configureClientSettings?.Invoke(clientSettings);
clientSettings.LoggingSettings ??= new LoggingSettings(serviceProvider.GetService());
var aspireVersion = typeof(MongoDBSettings).Assembly.GetName().Version?.ToString();
if (clientSettings.LibraryInfo != null)
{
clientSettings.LibraryInfo = new LibraryInfo($"{clientSettings.LibraryInfo.Name}|aspire", $"{clientSettings.LibraryInfo.Version}|{aspireVersion}");
}
else
{
clientSettings.LibraryInfo = new LibraryInfo("aspire", aspireVersion);
}
return new MongoClient(clientSettings);
}
private static MongoDBSettings GetMongoDBSettings(
this IHostApplicationBuilder builder,
string connectionName,
Action? configureSettings)
{
var settings = new MongoDBSettings();
var configSection = builder.Configuration.GetSection(DefaultConfigSectionName);
var namedConfigSection = configSection.GetSection(connectionName);
configSection.Bind(settings);
namedConfigSection.Bind(settings);
if (builder.Configuration.TryGetConnectionString(connectionName, out var connectionString))
{
settings.ConnectionString = connectionString;
}
configureSettings?.Invoke(settings);
return settings;
}
private static void ValidateSettings(
this MongoDBSettings settings,
string connectionName)
{
ConnectionStringValidation.ValidateConnectionString(settings.ConnectionString, connectionName, DefaultConfigSectionName);
}
}