| File: AspirePostgreSqlNpgsqlExtensions.cs | Web Access |
| Project: src\src\Components\Aspire.Npgsql\Aspire.Npgsql.csproj (Aspire.Npgsql) |
// 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.Npgsql; using HealthChecks.NpgSql; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Npgsql; namespace Microsoft.Extensions.Hosting; /// <summary> /// Extension methods for connecting PostgreSQL database with Npgsql client /// </summary> public static class AspirePostgreSqlNpgsqlExtensions { private const string DefaultConfigSectionName = "Aspire:Npgsql"; /// <summary> /// Registers <see cref="NpgsqlDataSource"/> service for connecting PostgreSQL database with Npgsql client. /// Configures health check, logging and telemetry for the Npgsql client. /// </summary> /// <param name="builder">The <see cref="IHostApplicationBuilder" /> to read config from and add services to.</param> /// <param name="connectionName">A name used to retrieve the connection string from the ConnectionStrings configuration section.</param> /// <param name="configureSettings">An optional delegate that can be used for customizing options. It's invoked after the settings are read from the configuration.</param> /// <param name="configureDataSourceBuilder">An optional delegate that can be used for customizing the <see cref="NpgsqlDataSourceBuilder"/>.</param> /// <remarks>Reads the configuration from "Aspire:Npgsql" section.</remarks> /// <exception cref="ArgumentNullException">Thrown if mandatory <paramref name="builder"/> is null.</exception> /// <exception cref="InvalidOperationException">Thrown when mandatory <see cref="NpgsqlSettings.ConnectionString"/> is not provided.</exception> public static void AddNpgsqlDataSource(this IHostApplicationBuilder builder, string connectionName, Action<NpgsqlSettings>? configureSettings = null, Action<NpgsqlDataSourceBuilder>? configureDataSourceBuilder = null) => AddNpgsqlDataSource(builder, configureSettings, connectionName, serviceKey: null, configureDataSourceBuilder: configureDataSourceBuilder); /// <summary> /// Registers <see cref="NpgsqlDataSource"/> as a keyed service for given <paramref name="name"/> for connecting PostgreSQL database with Npgsql client. /// Configures health check, logging and telemetry for the Npgsql client. /// </summary> /// <param name="builder">The <see cref="IHostApplicationBuilder" /> to read config from and add services to.</param> /// <param name="name">The name of the component, which is used as the <see cref="ServiceDescriptor.ServiceKey"/> of the service and also to retrieve the connection string from the ConnectionStrings configuration section.</param> /// <param name="configureSettings">An optional method that can be used for customizing options. It's invoked after the settings are read from the configuration.</param> /// <param name="configureDataSourceBuilder">An optional delegate that can be used for customizing the <see cref="NpgsqlDataSourceBuilder"/>.</param> /// <remarks>Reads the configuration from "Aspire:Npgsql:{name}" section.</remarks> /// <exception cref="ArgumentNullException">Thrown when <paramref name="builder"/> or <paramref name="name"/> is null.</exception> /// <exception cref="ArgumentException">Thrown if mandatory <paramref name="name"/> is empty.</exception> /// <exception cref="InvalidOperationException">Thrown when mandatory <see cref="NpgsqlSettings.ConnectionString"/> is not provided.</exception> public static void AddKeyedNpgsqlDataSource(this IHostApplicationBuilder builder, string name, Action<NpgsqlSettings>? configureSettings = null, Action<NpgsqlDataSourceBuilder>? configureDataSourceBuilder = null) { ArgumentException.ThrowIfNullOrEmpty(name); AddNpgsqlDataSource(builder, configureSettings, connectionName: name, serviceKey: name, configureDataSourceBuilder: configureDataSourceBuilder); } private static void AddNpgsqlDataSource(IHostApplicationBuilder builder, Action<NpgsqlSettings>? configureSettings, string connectionName, object? serviceKey, Action<NpgsqlDataSourceBuilder>? configureDataSourceBuilder) { ArgumentNullException.ThrowIfNull(builder); ArgumentException.ThrowIfNullOrEmpty(connectionName); NpgsqlSettings settings = new(); var configSection = builder.Configuration.GetSection(DefaultConfigSectionName); var namedConfigSection = configSection.GetSection(connectionName); configSection.Bind(settings); namedConfigSection.Bind(settings); if (builder.Configuration.GetConnectionString(connectionName) is string connectionString) { settings.ConnectionString = connectionString; } configureSettings?.Invoke(settings); builder.RegisterNpgsqlServices(settings, connectionName, serviceKey, configureDataSourceBuilder); // Same as SqlClient connection pooling is on by default and can be handled with connection string // https://www.npgsql.org/doc/connection-string-parameters.html#pooling if (!settings.DisableHealthChecks) { builder.TryAddHealthCheck(new HealthCheckRegistration( serviceKey is null ? "PostgreSql" : $"PostgreSql_{connectionName}", sp => new NpgSqlHealthCheck( new NpgSqlHealthCheckOptions(serviceKey is null ? sp.GetRequiredService<NpgsqlDataSource>() : sp.GetRequiredKeyedService<NpgsqlDataSource>(serviceKey))), failureStatus: default, tags: default, timeout: default)); } if (!settings.DisableTracing) { builder.Services.AddOpenTelemetry() .WithTracing(tracerProviderBuilder => { tracerProviderBuilder.AddNpgsql(); }); } if (!settings.DisableMetrics) { builder.Services.AddOpenTelemetry() .WithMetrics(NpgsqlCommon.AddNpgsqlMetrics); } } private static void RegisterNpgsqlServices(this IHostApplicationBuilder builder, NpgsqlSettings settings, string connectionName, object? serviceKey, Action<NpgsqlDataSourceBuilder>? configureDataSourceBuilder) { builder.Services.AddNpgsqlDataSource(settings.ConnectionString ?? string.Empty, dataSourceBuilder => { // delay validating the ConnectionString until the DataSource is requested. This ensures an exception doesn't happen until a Logger is established. ConnectionStringValidation.ValidateConnectionString(settings.ConnectionString, connectionName, DefaultConfigSectionName); configureDataSourceBuilder?.Invoke(dataSourceBuilder); }, serviceKey: serviceKey); } }