X Tutup
using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Npgsql.Internal; using Npgsql.Internal.Resolvers; using Npgsql.TypeMapping; namespace Npgsql; /// /// Provides a simple API for configuring and creating an , from which database connections can be obtained. /// public sealed class NpgsqlDataSourceBuilder : INpgsqlTypeMapper { static UnsupportedTypeInfoResolver UnsupportedTypeInfoResolver { get; } = new(); readonly NpgsqlSlimDataSourceBuilder _internalBuilder; /// /// A diagnostics name used by Npgsql when generating tracing, logging and metrics. /// public string? Name { get => _internalBuilder.Name; set => _internalBuilder.Name = value; } /// public INpgsqlNameTranslator DefaultNameTranslator { get => _internalBuilder.DefaultNameTranslator; set => _internalBuilder.DefaultNameTranslator = value; } /// /// A connection string builder that can be used to configured the connection string on the builder. /// public NpgsqlConnectionStringBuilder ConnectionStringBuilder => _internalBuilder.ConnectionStringBuilder; /// /// Returns the connection string, as currently configured on the builder. /// public string ConnectionString => _internalBuilder.ConnectionString; internal static void ResetGlobalMappings(bool overwrite) => GlobalTypeMapper.Instance.AddGlobalTypeMappingResolvers(new IPgTypeInfoResolver[] { overwrite ? new AdoTypeInfoResolver() : AdoTypeInfoResolver.Instance, new ExtraConversionsResolver(), new JsonTypeInfoResolver(), new RangeTypeInfoResolver(), new RecordTypeInfoResolver(), new FullTextSearchTypeInfoResolver(), new NetworkTypeInfoResolver(), new GeometricTypeInfoResolver(), new LTreeTypeInfoResolver(), // Arrays new AdoArrayTypeInfoResolver(), new ExtraConversionsArrayTypeInfoResolver(), new JsonArrayTypeInfoResolver(), new RangeArrayTypeInfoResolver(), new RecordArrayTypeInfoResolver(), new FullTextSearchArrayTypeInfoResolver(), new NetworkArrayTypeInfoResolver(), new GeometricArrayTypeInfoResolver(), new LTreeArrayTypeInfoResolver() }, overwrite); static NpgsqlDataSourceBuilder() => ResetGlobalMappings(overwrite: false); /// /// Constructs a new , optionally starting out from the given . /// public NpgsqlDataSourceBuilder(string? connectionString = null) { _internalBuilder = new(new NpgsqlConnectionStringBuilder(connectionString)); AddDefaultFeatures(); void AddDefaultFeatures() { _internalBuilder.EnableTransportSecurity(); _internalBuilder.EnableIntegratedSecurity(); AddTypeInfoResolver(UnsupportedTypeInfoResolver); // Reverse order arrays. AddTypeInfoResolver(new LTreeArrayTypeInfoResolver()); AddTypeInfoResolver(new GeometricArrayTypeInfoResolver()); AddTypeInfoResolver(new NetworkArrayTypeInfoResolver()); AddTypeInfoResolver(new FullTextSearchArrayTypeInfoResolver()); AddTypeInfoResolver(new RecordArrayTypeInfoResolver()); AddTypeInfoResolver(new RangeArrayTypeInfoResolver()); AddTypeInfoResolver(new JsonArrayTypeInfoResolver()); AddTypeInfoResolver(new ExtraConversionsArrayTypeInfoResolver()); AddTypeInfoResolver(new AdoArrayTypeInfoResolver()); // Reverse order. AddTypeInfoResolver(new LTreeTypeInfoResolver()); AddTypeInfoResolver(new GeometricTypeInfoResolver()); AddTypeInfoResolver(new NetworkTypeInfoResolver()); AddTypeInfoResolver(new FullTextSearchTypeInfoResolver()); AddTypeInfoResolver(new RecordTypeInfoResolver()); AddTypeInfoResolver(new RangeTypeInfoResolver()); AddTypeInfoResolver(new JsonTypeInfoResolver()); AddTypeInfoResolver(new ExtraConversionsResolver()); AddTypeInfoResolver(AdoTypeInfoResolver.Instance); var plugins = new List(GlobalTypeMapper.Instance.GetPluginResolvers()); plugins.Reverse(); foreach (var plugin in plugins) AddTypeInfoResolver(plugin); } } /// /// Sets the that will be used for logging. /// /// The logger factory to be used. /// The same builder instance so that multiple calls can be chained. public NpgsqlDataSourceBuilder UseLoggerFactory(ILoggerFactory? loggerFactory) { _internalBuilder.UseLoggerFactory(loggerFactory); return this; } /// /// Enables parameters to be included in logging. This includes potentially sensitive information from data sent to PostgreSQL. /// You should only enable this flag in development, or if you have the appropriate security measures in place based on the /// sensitivity of this data. /// /// If , then sensitive data is logged. /// The same builder instance so that multiple calls can be chained. public NpgsqlDataSourceBuilder EnableParameterLogging(bool parameterLoggingEnabled = true) { _internalBuilder.EnableParameterLogging(parameterLoggingEnabled); return this; } #region Authentication /// /// When using SSL/TLS, this is a callback that allows customizing how the PostgreSQL-provided certificate is verified. This is an /// advanced API, consider using or instead. /// /// The callback containing custom callback verification logic. /// /// /// Cannot be used in conjunction with , or /// . /// /// /// See . /// /// /// The same builder instance so that multiple calls can be chained. public NpgsqlDataSourceBuilder UseUserCertificateValidationCallback(RemoteCertificateValidationCallback userCertificateValidationCallback) { _internalBuilder.UseUserCertificateValidationCallback(userCertificateValidationCallback); return this; } /// /// Specifies an SSL/TLS certificate which Npgsql will send to PostgreSQL for certificate-based authentication. /// /// The client certificate to be sent to PostgreSQL when opening a connection. /// The same builder instance so that multiple calls can be chained. public NpgsqlDataSourceBuilder UseClientCertificate(X509Certificate? clientCertificate) { _internalBuilder.UseClientCertificate(clientCertificate); return this; } /// /// Specifies a collection of SSL/TLS certificates which Npgsql will send to PostgreSQL for certificate-based authentication. /// /// The client certificate collection to be sent to PostgreSQL when opening a connection. /// The same builder instance so that multiple calls can be chained. public NpgsqlDataSourceBuilder UseClientCertificates(X509CertificateCollection? clientCertificates) { _internalBuilder.UseClientCertificates(clientCertificates); return this; } /// /// Specifies a callback to modify the collection of SSL/TLS client certificates which Npgsql will send to PostgreSQL for /// certificate-based authentication. This is an advanced API, consider using or /// instead. /// /// The callback to modify the client certificate collection. /// /// /// The callback is invoked every time a physical connection is opened, and is therefore suitable for rotating short-lived client /// certificates. Simply make sure the certificate collection argument has the up-to-date certificate(s). /// /// /// The callback's collection argument already includes any client certificates specified via the connection string or environment /// variables. /// /// /// The same builder instance so that multiple calls can be chained. public NpgsqlDataSourceBuilder UseClientCertificatesCallback(Action? clientCertificatesCallback) { _internalBuilder.UseClientCertificatesCallback(clientCertificatesCallback); return this; } /// /// Sets the that will be used validate SSL certificate, received from the server. /// /// The CA certificate. /// The same builder instance so that multiple calls can be chained. public NpgsqlDataSourceBuilder UseRootCertificate(X509Certificate2? rootCertificate) { _internalBuilder.UseRootCertificate(rootCertificate); return this; } /// /// Specifies a callback that will be used to validate SSL certificate, received from the server. /// /// The callback to get CA certificate. /// The same builder instance so that multiple calls can be chained. /// /// This overload, which accepts a callback, is suitable for scenarios where the certificate rotates /// and might change during the lifetime of the application. /// When that's not the case, use the overload which directly accepts the certificate. /// public NpgsqlDataSourceBuilder UseRootCertificateCallback(Func? rootCertificateCallback) { _internalBuilder.UseRootCertificateCallback(rootCertificateCallback); return this; } /// /// Configures a periodic password provider, which is automatically called by the data source at some regular interval. This is the /// recommended way to fetch a rotating access token. /// /// A callback which returns the password to be sent to PostgreSQL. /// How long to cache the password before re-invoking the callback. /// /// If a password refresh attempt fails, it will be re-attempted with this interval. /// This should typically be much lower than . /// /// The same builder instance so that multiple calls can be chained. /// /// /// The provided callback is invoked in a timer, and not when opening connections. It therefore doesn't affect opening time. /// /// /// The provided cancellation token is only triggered when the entire data source is disposed. If you'd like to apply a timeout to the /// token fetching, do so within the provided callback. /// /// public NpgsqlDataSourceBuilder UsePeriodicPasswordProvider( Func>? passwordProvider, TimeSpan successRefreshInterval, TimeSpan failureRefreshInterval) { _internalBuilder.UsePeriodicPasswordProvider(passwordProvider, successRefreshInterval, failureRefreshInterval); return this; } #endregion Authentication #region Type mapping /// public void AddTypeInfoResolver(IPgTypeInfoResolver resolver) => _internalBuilder.AddTypeInfoResolver(resolver); /// void INpgsqlTypeMapper.Reset() => _internalBuilder.ResetTypeMappings(); /// public INpgsqlTypeMapper MapEnum<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum>(string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) where TEnum : struct, Enum { _internalBuilder.MapEnum(pgName, nameTranslator); return this; } /// public bool UnmapEnum<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum>(string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) where TEnum : struct, Enum => _internalBuilder.UnmapEnum(pgName, nameTranslator); /// [RequiresDynamicCode("Calling MapEnum with a Type can require creating new generic types or methods. This may not work when AOT compiling.")] public INpgsqlTypeMapper MapEnum([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type clrType, string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) => _internalBuilder.MapEnum(clrType, pgName, nameTranslator); /// public bool UnmapEnum([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type clrType, string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) => _internalBuilder.UnmapEnum(clrType, pgName, nameTranslator); /// [RequiresDynamicCode("Mapping composite types involves serializing arbitrary types, requiring require creating new generic types or methods. This is currently unsupported with NativeAOT, vote on issue #5303 if this is important to you.")] public INpgsqlTypeMapper MapComposite<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] T>( string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) { _internalBuilder.MapComposite(pgName, nameTranslator); return this; } /// [RequiresDynamicCode("Mapping composite types involves serializing arbitrary types, requiring require creating new generic types or methods. This is currently unsupported with NativeAOT, vote on issue #5303 if this is important to you.")] public INpgsqlTypeMapper MapComposite([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] Type clrType, string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) { _internalBuilder.MapComposite(clrType, pgName, nameTranslator); return this; } /// [RequiresDynamicCode("Mapping composite types involves serializing arbitrary types, requiring require creating new generic types or methods. This is currently unsupported with NativeAOT, vote on issue #5303 if this is important to you.")] public bool UnmapComposite<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] T>( string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) => _internalBuilder.UnmapComposite(pgName, nameTranslator); /// [RequiresDynamicCode("Mapping composite types involves serializing arbitrary types, requiring require creating new generic types or methods. This is currently unsupported with NativeAOT, vote on issue #5303 if this is important to you.")] public bool UnmapComposite([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] Type clrType, string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) => _internalBuilder.UnmapComposite(clrType, pgName, nameTranslator); #endregion Type mapping /// /// Register a connection initializer, which allows executing arbitrary commands when a physical database connection is first opened. /// /// /// A synchronous connection initialization lambda, which will be called from when a new physical /// connection is opened. /// /// /// An asynchronous connection initialization lambda, which will be called from /// when a new physical connection is opened. /// /// /// If an initializer is registered, both sync and async versions must be provided. If you do not use sync APIs in your code, simply /// throw , which would also catch accidental cases of sync opening. /// /// /// Take care that the setting you apply in the initializer does not get reverted when the connection is returned to the pool, since /// Npgsql sends DISCARD ALL by default. The option can be used to /// turn this off. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlDataSourceBuilder UsePhysicalConnectionInitializer( Action? connectionInitializer, Func? connectionInitializerAsync) { _internalBuilder.UsePhysicalConnectionInitializer(connectionInitializer, connectionInitializerAsync); return this; } /// /// Builds and returns an which is ready for use. /// public NpgsqlDataSource Build() => _internalBuilder.Build(); /// /// Builds and returns a which is ready for use for load-balancing and failover scenarios. /// public NpgsqlMultiHostDataSource BuildMultiHost() => _internalBuilder.BuildMultiHost(); }
X Tutup