X Tutup
using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Text.Json; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Npgsql.Internal; using Npgsql.Internal.ResolverFactories; using Npgsql.NameTranslation; using Npgsql.Properties; using Npgsql.TypeMapping; using NpgsqlTypes; namespace Npgsql; /// /// Provides a simple API for configuring and creating an , from which database connections can be obtained. /// /// /// On this builder, various features are disabled by default; unless you're looking to save on code size (e.g. when publishing with /// NativeAOT), use instead. /// public sealed class NpgsqlSlimDataSourceBuilder : INpgsqlTypeMapper { static UnsupportedTypeInfoResolver UnsupportedTypeInfoResolver { get; } = new(); ILoggerFactory? _loggerFactory; bool _sensitiveDataLoggingEnabled; List>? _tracingOptionsBuilderCallbacks; List>? _typeLoadingOptionsBuilderCallbacks; TransportSecurityHandler _transportSecurityHandler = new(); RemoteCertificateValidationCallback? _userCertificateValidationCallback; Action? _clientCertificatesCallback; Action? _sslClientAuthenticationOptionsCallback; Action? _negotiateOptionsCallback; IntegratedSecurityHandler _integratedSecurityHandler = new(); Func? _passwordProvider; Func>? _passwordProviderAsync; Func>? _periodicPasswordProvider; TimeSpan _periodicPasswordSuccessRefreshInterval, _periodicPasswordFailureRefreshInterval; List? _dbTypeResolverFactories; PgTypeInfoResolverChainBuilder _resolverChainBuilder = new(); // mutable struct, don't make readonly. readonly UserTypeMapper _userTypeMapper; Action? _connectionInitializer; Func? _connectionInitializerAsync; internal JsonSerializerOptions? JsonSerializerOptions { get; private set; } internal Action ConfigureDefaultFactories { get; set; } /// /// A connection string builder that can be used to configure the connection string on the builder. /// public NpgsqlConnectionStringBuilder ConnectionStringBuilder { get; } /// /// Returns the connection string, as currently configured on the builder. /// public string ConnectionString => ConnectionStringBuilder.ToString(); static NpgsqlSlimDataSourceBuilder() => GlobalTypeMapper.Instance.AddGlobalTypeMappingResolvers([new AdoTypeInfoResolverFactory()]); /// /// A diagnostics name used by Npgsql when generating tracing, logging and metrics. /// public string? Name { get; set; } /// /// Constructs a new , optionally starting out from the given /// . /// public NpgsqlSlimDataSourceBuilder(string? connectionString = null) : this(new NpgsqlConnectionStringBuilder(connectionString)) {} internal NpgsqlSlimDataSourceBuilder(NpgsqlConnectionStringBuilder connectionStringBuilder) { ConnectionStringBuilder = connectionStringBuilder; _userTypeMapper = new() { DefaultNameTranslator = GlobalTypeMapper.Instance.DefaultNameTranslator }; ConfigureDefaultFactories = static instance => instance.AppendDefaultFactories(); ConfigureResolverChain = static chain => chain.Add(UnsupportedTypeInfoResolver); } /// /// 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 NpgsqlSlimDataSourceBuilder UseLoggerFactory(ILoggerFactory? loggerFactory) { _loggerFactory = 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 NpgsqlSlimDataSourceBuilder EnableParameterLogging(bool parameterLoggingEnabled = true) { _sensitiveDataLoggingEnabled = parameterLoggingEnabled; return this; } /// /// Configure type loading options for the DataSource. Calling this again will replace /// the prior action. /// public NpgsqlSlimDataSourceBuilder ConfigureTypeLoading(Action configureAction) { ArgumentNullException.ThrowIfNull(configureAction); _typeLoadingOptionsBuilderCallbacks ??= new(); _typeLoadingOptionsBuilderCallbacks.Add(configureAction); return this; } /// /// Configures OpenTelemetry tracing options. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder ConfigureTracing(Action configureAction) { ArgumentNullException.ThrowIfNull(configureAction); _tracingOptionsBuilderCallbacks ??= new(); _tracingOptionsBuilderCallbacks.Add(configureAction); return this; } /// /// Configures the JSON serializer options used when reading and writing all System.Text.Json data. /// /// Options to customize JSON serialization and deserialization. /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder ConfigureJsonOptions(JsonSerializerOptions serializerOptions) { ArgumentNullException.ThrowIfNull(serializerOptions); JsonSerializerOptions = serializerOptions; 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. [Obsolete("Use UseSslClientAuthenticationOptionsCallback")] public NpgsqlSlimDataSourceBuilder UseUserCertificateValidationCallback( RemoteCertificateValidationCallback userCertificateValidationCallback) { _userCertificateValidationCallback = 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. [Obsolete("Use UseSslClientAuthenticationOptionsCallback")] public NpgsqlSlimDataSourceBuilder UseClientCertificate(X509Certificate? clientCertificate) { if (clientCertificate is null) return UseClientCertificatesCallback(null); var clientCertificates = new X509CertificateCollection { clientCertificate }; return UseClientCertificates(clientCertificates); } /// /// 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. [Obsolete("Use UseSslClientAuthenticationOptionsCallback")] public NpgsqlSlimDataSourceBuilder UseClientCertificates(X509CertificateCollection? clientCertificates) => UseClientCertificatesCallback(clientCertificates is null ? null : certs => certs.AddRange(clientCertificates)); /// /// When using SSL/TLS, this is a callback that allows customizing SslStream's authentication options. /// /// The callback to customize SslStream's authentication options. /// /// /// See . /// /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder UseSslClientAuthenticationOptionsCallback(Action? sslClientAuthenticationOptionsCallback) { _sslClientAuthenticationOptionsCallback = sslClientAuthenticationOptionsCallback; 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. [Obsolete("Use UseSslClientAuthenticationOptionsCallback")] public NpgsqlSlimDataSourceBuilder UseClientCertificatesCallback(Action? clientCertificatesCallback) { _clientCertificatesCallback = 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 NpgsqlSlimDataSourceBuilder UseRootCertificate(X509Certificate2? rootCertificate) => rootCertificate is null ? UseRootCertificatesCallback((Func?)null) : UseRootCertificateCallback(() => rootCertificate); /// /// Sets the that will be used validate SSL certificate, received from the server. /// /// The CA certificates. /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder UseRootCertificates(X509Certificate2Collection? rootCertificates) => rootCertificates is null ? UseRootCertificatesCallback((Func?)null) : UseRootCertificatesCallback(() => rootCertificates); /// /// 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. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder UseRootCertificateCallback(Func? rootCertificateCallback) { _transportSecurityHandler.RootCertificatesCallback = () => rootCertificateCallback is not null ? new X509Certificate2Collection(rootCertificateCallback()) : null; return this; } /// /// Specifies a callback that will be used to validate SSL certificate, received from the server. /// /// The callback to get CA certificates. /// 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. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder UseRootCertificatesCallback(Func? rootCertificateCallback) { _transportSecurityHandler.RootCertificatesCallback = 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. /// /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder UsePeriodicPasswordProvider( Func>? passwordProvider, TimeSpan successRefreshInterval, TimeSpan failureRefreshInterval) { if (successRefreshInterval < TimeSpan.Zero) throw new ArgumentException( string.Format(NpgsqlStrings.ArgumentMustBePositive, nameof(successRefreshInterval)), nameof(successRefreshInterval)); if (failureRefreshInterval < TimeSpan.Zero) throw new ArgumentException( string.Format(NpgsqlStrings.ArgumentMustBePositive, nameof(failureRefreshInterval)), nameof(failureRefreshInterval)); _periodicPasswordProvider = passwordProvider; _periodicPasswordSuccessRefreshInterval = successRefreshInterval; _periodicPasswordFailureRefreshInterval = failureRefreshInterval; return this; } /// /// Configures a password provider, which is called by the data source when opening connections. /// /// /// A callback that may be invoked during which returns the password to be sent to PostgreSQL. /// /// /// A callback that may be invoked during which returns the password to be sent to PostgreSQL. /// /// The same builder instance so that multiple calls can be chained. /// /// /// The provided callback is invoked when opening connections. Therefore its important the callback internally depends on cached /// data or returns quickly otherwise. Any unnecessary delay will affect connection opening time. /// /// public NpgsqlSlimDataSourceBuilder UsePasswordProvider( Func? passwordProvider, Func>? passwordProviderAsync) { if (passwordProvider is null != passwordProviderAsync is null) throw new ArgumentException(NpgsqlStrings.SyncAndAsyncPasswordProvidersRequired); _passwordProvider = passwordProvider; _passwordProviderAsync = passwordProviderAsync; return this; } /// /// When using Kerberos, this is a callback that allows customizing default settings for Kerberos authentication. /// /// The callback containing logic to customize Kerberos authentication settings. /// /// /// See . /// /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder UseNegotiateOptionsCallback(Action? negotiateOptionsCallback) { _negotiateOptionsCallback = negotiateOptionsCallback; return this; } #endregion Authentication #region Type mapping /// public INpgsqlNameTranslator DefaultNameTranslator { get => _userTypeMapper.DefaultNameTranslator; set => _userTypeMapper.DefaultNameTranslator = value; } /// /// Maps a CLR enum to a PostgreSQL enum type. /// /// /// CLR enum labels are mapped by name to PostgreSQL enum labels. /// The translation strategy can be controlled by the parameter, /// which defaults to . /// You can also use the on your enum fields to manually specify a PostgreSQL enum label. /// If there is a discrepancy between the .NET and database labels while an enum is read or written, /// an exception will be raised. /// /// /// A PostgreSQL type name for the corresponding enum type in the database. /// If null, the name translator given in will be used. /// /// /// A component which will be used to translate CLR names (e.g. SomeClass) into database names (e.g. some_class). /// Defaults to . /// /// The .NET enum type to be mapped public NpgsqlSlimDataSourceBuilder MapEnum<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum>(string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) where TEnum : struct, Enum { _userTypeMapper.MapEnum(pgName, nameTranslator); return this; } /// public bool UnmapEnum<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum>(string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) where TEnum : struct, Enum => _userTypeMapper.UnmapEnum(pgName, nameTranslator); /// /// Maps a CLR enum to a PostgreSQL enum type. /// /// /// CLR enum labels are mapped by name to PostgreSQL enum labels. /// The translation strategy can be controlled by the parameter, /// which defaults to . /// You can also use the on your enum fields to manually specify a PostgreSQL enum label. /// If there is a discrepancy between the .NET and database labels while an enum is read or written, /// an exception will be raised. /// /// The .NET enum type to be mapped /// /// A PostgreSQL type name for the corresponding enum type in the database. /// If null, the name translator given in will be used. /// /// /// A component which will be used to translate CLR names (e.g. SomeClass) into database names (e.g. some_class). /// Defaults to . /// [RequiresDynamicCode("Calling MapEnum with a Type can require creating new generic types or methods. This may not work when AOT compiling.")] public NpgsqlSlimDataSourceBuilder MapEnum([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type clrType, string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) { _userTypeMapper.MapEnum(clrType, pgName, nameTranslator); return this; } /// public bool UnmapEnum([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type clrType, string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) => _userTypeMapper.UnmapEnum(clrType, pgName, nameTranslator); /// /// Maps a CLR type to a PostgreSQL composite type. /// /// /// CLR fields and properties by string to PostgreSQL names. /// The translation strategy can be controlled by the parameter, /// which defaults to . /// You can also use the on your members to manually specify a PostgreSQL name. /// If there is a discrepancy between the .NET type and database type while a composite is read or written, /// an exception will be raised. /// /// /// A PostgreSQL type name for the corresponding composite type in the database. /// If null, the name translator given in will be used. /// /// /// A component which will be used to translate CLR names (e.g. SomeClass) into database names (e.g. some_class). /// Defaults to . /// /// The .NET type to be mapped [RequiresDynamicCode("Mapping composite types involves serializing arbitrary types which can require creating new generic types or methods. This is currently unsupported with NativeAOT, vote on issue #5303 if this is important to you.")] public NpgsqlSlimDataSourceBuilder MapComposite<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] T>( string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) { _userTypeMapper.MapComposite(typeof(T), pgName, nameTranslator); return this; } /// [RequiresDynamicCode("Mapping composite types involves serializing arbitrary types which can 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) => _userTypeMapper.UnmapComposite(typeof(T), pgName, nameTranslator); /// /// Maps a CLR type to a composite type. /// /// /// Maps CLR fields and properties by string to PostgreSQL names. /// The translation strategy can be controlled by the parameter, /// which defaults to . /// If there is a discrepancy between the .NET type and database type while a composite is read or written, /// an exception will be raised. /// /// The .NET type to be mapped. /// /// A PostgreSQL type name for the corresponding composite type in the database. /// If null, the name translator given in will be used. /// /// /// A component which will be used to translate CLR names (e.g. SomeClass) into database names (e.g. some_class). /// Defaults to . /// [RequiresDynamicCode("Mapping composite types involves serializing arbitrary types which can require creating new generic types or methods. This is currently unsupported with NativeAOT, vote on issue #5303 if this is important to you.")] public NpgsqlSlimDataSourceBuilder MapComposite([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] Type clrType, string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) { _userTypeMapper.MapComposite(clrType, pgName, nameTranslator); return this; } /// [RequiresDynamicCode("Mapping composite types involves serializing arbitrary types which can 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) => _userTypeMapper.UnmapComposite(clrType, pgName, nameTranslator); /// void INpgsqlTypeMapper.AddDbTypeResolverFactory(DbTypeResolverFactory factory) => (_dbTypeResolverFactories ??= new()).Add(factory); /// [Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public void AddTypeInfoResolverFactory(PgTypeInfoResolverFactory factory) => _resolverChainBuilder.PrependResolverFactory(factory); /// void INpgsqlTypeMapper.Reset() => _resolverChainBuilder.Clear(); internal Action> ConfigureResolverChain { get; set; } internal void AppendResolverFactory(PgTypeInfoResolverFactory factory) => _resolverChainBuilder.AppendResolverFactory(factory); internal void AppendResolverFactory(Func factory) where T : PgTypeInfoResolverFactory => _resolverChainBuilder.AppendResolverFactory(factory); internal void AppendDefaultFactories() { // When used publicly we start off with our slim defaults. _resolverChainBuilder.AppendResolverFactory(_userTypeMapper); if (GlobalTypeMapper.Instance.GetUserMappingsResolverFactory() is { } userMappingsResolverFactory) _resolverChainBuilder.AppendResolverFactory(userMappingsResolverFactory); foreach (var factory in GlobalTypeMapper.Instance.GetPluginResolverFactories()) _resolverChainBuilder.AppendResolverFactory(factory); _resolverChainBuilder.AppendResolverFactory(new AdoTypeInfoResolverFactory()); } #endregion Type mapping #region Optional opt-ins /// /// Sets up mappings for the PostgreSQL array types. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableArrays() { _resolverChainBuilder.EnableArrays(); return this; } /// /// Sets up mappings for the PostgreSQL range types. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableRanges() { _resolverChainBuilder.EnableRanges(); return this; } /// /// Sets up mappings for the PostgreSQL multirange types. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableMultiranges() { _resolverChainBuilder.EnableMultiranges(); return this; } /// /// Sets up mappings for the PostgreSQL record type as a .NET object[]. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableRecords() { AddTypeInfoResolverFactory(new RecordTypeInfoResolverFactory()); return this; } /// /// Sets up mappings for the PostgreSQL tsquery and tsvector types. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableFullTextSearch() { AddTypeInfoResolverFactory(new FullTextSearchTypeInfoResolverFactory()); return this; } /// /// Sets up mappings for the PostgreSQL ltree extension types. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableLTree() { AddTypeInfoResolverFactory(new LTreeTypeInfoResolverFactory()); return this; } /// /// Sets up mappings for the PostgreSQL cube extension type. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableCube() { AddTypeInfoResolverFactory(new CubeTypeInfoResolverFactory()); return this; } /// /// Sets up mappings for extra conversions from PostgreSQL to .NET types. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableExtraConversions() { AddTypeInfoResolverFactory(new ExtraConversionResolverFactory()); return this; } /// /// Enables the possibility to use TLS/SSl encryption for connections to PostgreSQL. This does not guarantee that encryption will /// actually be used; see for more details. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableTransportSecurity() { _transportSecurityHandler = new RealTransportSecurityHandler(); return this; } /// /// Enables the possibility to use GSS/SSPI authentication and encryption for connections to PostgreSQL. This does not guarantee that it will /// actually be used; see for more details. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableIntegratedSecurity() { _integratedSecurityHandler = new RealIntegratedSecurityHandler(); return this; } /// /// Sets up network mappings. This allows mapping PhysicalAddress, IPAddress, NpgsqlInet and NpgsqlCidr types /// to PostgreSQL macaddr, macaddr8, inet and cidr types. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableNetworkTypes() { _resolverChainBuilder.AppendResolverFactory(new NetworkTypeInfoResolverFactory()); return this; } /// /// Sets up network mappings. This allows mapping types like NpgsqlPoint and NpgsqlPath /// to PostgreSQL point, path and so on types. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableGeometricTypes() { _resolverChainBuilder.AppendResolverFactory(new GeometricTypeInfoResolverFactory()); return this; } /// /// Sets up System.Text.Json mappings. This allows mapping JsonDocument and JsonElement types to PostgreSQL json and jsonb /// types. /// /// The same builder instance so that multiple calls can be chained. public NpgsqlSlimDataSourceBuilder EnableJsonTypes() { _resolverChainBuilder.AppendResolverFactory(() => new JsonTypeInfoResolverFactory(JsonSerializerOptions)); return this; } /// /// Sets up dynamic System.Text.Json mappings. This allows mapping arbitrary .NET types to PostgreSQL json and jsonb /// types, as well as and its derived types. /// /// /// A list of CLR types to map to PostgreSQL jsonb (no need to specify ). /// /// /// A list of CLR types to map to PostgreSQL json (no need to specify ). /// /// /// Due to the dynamic nature of these mappings, they are not compatible with NativeAOT or trimming. /// /// The same builder instance so that multiple calls can be chained. [RequiresUnreferencedCode("Json serializer may perform reflection on trimmed types.")] [RequiresDynamicCode("Serializing arbitrary types to json can require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling.")] public NpgsqlSlimDataSourceBuilder EnableDynamicJson( Type[]? jsonbClrTypes = null, Type[]? jsonClrTypes = null) { _resolverChainBuilder.AppendResolverFactory(() => new JsonDynamicTypeInfoResolverFactory(jsonbClrTypes, jsonClrTypes, JsonSerializerOptions)); return this; } /// /// Sets up mappings for the PostgreSQL record type as a .NET or . /// /// The same builder instance so that multiple calls can be chained. [RequiresUnreferencedCode("The mapping of PostgreSQL records as .NET tuples requires reflection usage which is incompatible with trimming.")] [RequiresDynamicCode("The mapping of PostgreSQL records as .NET tuples requires dynamic code usage which is incompatible with NativeAOT.")] public NpgsqlSlimDataSourceBuilder EnableRecordsAsTuples() { AddTypeInfoResolverFactory(new TupledRecordTypeInfoResolverFactory()); return this; } /// /// Sets up mappings allowing the use of unmapped enum, range and multirange types. /// /// The same builder instance so that multiple calls can be chained. [RequiresUnreferencedCode("The use of unmapped enums, ranges or multiranges requires reflection usage which is incompatible with trimming.")] [RequiresDynamicCode("The use of unmapped enums, ranges or multiranges requires dynamic code usage which is incompatible with NativeAOT.")] public NpgsqlSlimDataSourceBuilder EnableUnmappedTypes() { AddTypeInfoResolverFactory(new UnmappedTypeInfoResolverFactory()); return this; } #endregion Optional opt-ins /// /// 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 NpgsqlSlimDataSourceBuilder UsePhysicalConnectionInitializer( Action? connectionInitializer, Func? connectionInitializerAsync) { if (connectionInitializer is null != connectionInitializerAsync is null) throw new ArgumentException(NpgsqlStrings.SyncAndAsyncConnectionInitializersRequired); _connectionInitializer = connectionInitializer; _connectionInitializerAsync = connectionInitializerAsync; return this; } /// /// Builds and returns an which is ready for use. /// public NpgsqlDataSource Build() { ConnectionStringBuilder.PostProcessAndValidate(); var (connectionStringBuilder, config) = PrepareConfiguration(); if (ConnectionStringBuilder.Host!.Contains(',')) { ValidateMultiHost(); return new NpgsqlMultiHostDataSource(connectionStringBuilder, config); } return ConnectionStringBuilder.Pooling ? new PoolingDataSource(connectionStringBuilder, config) : new UnpooledDataSource(connectionStringBuilder, config); } /// /// Builds and returns a which is ready for use for load-balancing and failover scenarios. /// public NpgsqlMultiHostDataSource BuildMultiHost() { ConnectionStringBuilder.PostProcessAndValidate(); var (connectionStringBuilder, config) = PrepareConfiguration(); ValidateMultiHost(); return new(connectionStringBuilder, config); } // Used in testing. internal (NpgsqlConnectionStringBuilder, NpgsqlDataSourceConfiguration) PrepareConfiguration() { var connectionStringBuilder = ConnectionStringBuilder.Clone(); var sslClientAuthenticationOptionsCallback = _sslClientAuthenticationOptionsCallback; var hasCertificateCallbacks = _userCertificateValidationCallback is not null || _clientCertificatesCallback is not null; if (sslClientAuthenticationOptionsCallback is not null && hasCertificateCallbacks) { throw new NotSupportedException(NpgsqlStrings.SslClientAuthenticationOptionsCallbackWithOtherCallbacksNotSupported); } if (sslClientAuthenticationOptionsCallback is null && hasCertificateCallbacks) { sslClientAuthenticationOptionsCallback = options => { if (_clientCertificatesCallback is not null) { options.ClientCertificates ??= new X509Certificate2Collection(); _clientCertificatesCallback.Invoke(options.ClientCertificates); } if (_userCertificateValidationCallback is not null) { options.RemoteCertificateValidationCallback = _userCertificateValidationCallback; } }; } if (!_transportSecurityHandler.SupportEncryption && sslClientAuthenticationOptionsCallback is not null) { throw new InvalidOperationException(NpgsqlStrings.TransportSecurityDisabled); } if (_passwordProvider is not null && _periodicPasswordProvider is not null) { throw new NotSupportedException(NpgsqlStrings.CannotSetMultiplePasswordProviderKinds); } if ((_passwordProvider is not null || _periodicPasswordProvider is not null) && (connectionStringBuilder.Password is not null || connectionStringBuilder.Passfile is not null)) { throw new NotSupportedException(NpgsqlStrings.CannotSetBothPasswordProviderAndPassword); } ConfigureDefaultFactories(this); var typeLoadingOptionsBuilder = new NpgsqlTypeLoadingOptionsBuilder(); #pragma warning disable CS0618 // Type or member is obsolete typeLoadingOptionsBuilder.EnableTableCompositesLoading(connectionStringBuilder.LoadTableComposites); typeLoadingOptionsBuilder.EnableTypeLoading(connectionStringBuilder.ServerCompatibilityMode is not ServerCompatibilityMode.NoTypeLoading); #pragma warning restore CS0618 // Type or member is obsolete foreach (var callback in _typeLoadingOptionsBuilderCallbacks ?? (IEnumerable>)[]) callback.Invoke(typeLoadingOptionsBuilder); var typeLoadingOptions = typeLoadingOptionsBuilder.Build(); var tracingOptionsBuilder = new NpgsqlTracingOptionsBuilder(); foreach (var callback in _tracingOptionsBuilderCallbacks ?? (IEnumerable>)[]) callback.Invoke(tracingOptionsBuilder); var tracingOptions = tracingOptionsBuilder.Build(); return (connectionStringBuilder, new( Name, _loggerFactory is null ? NpgsqlLoggingConfiguration.NullConfiguration : new NpgsqlLoggingConfiguration(_loggerFactory, _sensitiveDataLoggingEnabled), tracingOptions, typeLoadingOptions, _transportSecurityHandler, _integratedSecurityHandler, sslClientAuthenticationOptionsCallback, _passwordProvider, _passwordProviderAsync, _periodicPasswordProvider, _periodicPasswordSuccessRefreshInterval, _periodicPasswordFailureRefreshInterval, _resolverChainBuilder.Build(ConfigureResolverChain), _dbTypeResolverFactories ?? [], DefaultNameTranslator, _connectionInitializer, _connectionInitializerAsync, _negotiateOptionsCallback)); } void ValidateMultiHost() { if (ConnectionStringBuilder.ReplicationMode != ReplicationMode.Off) throw new NotSupportedException("Replication is not supported with multiple hosts"); } INpgsqlTypeMapper INpgsqlTypeMapper.ConfigureJsonOptions(JsonSerializerOptions serializerOptions) => ConfigureJsonOptions(serializerOptions); [RequiresUnreferencedCode("Json serializer may perform reflection on trimmed types.")] [RequiresDynamicCode( "Serializing arbitrary types to json can require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling.")] INpgsqlTypeMapper INpgsqlTypeMapper.EnableDynamicJson(Type[]? jsonbClrTypes, Type[]? jsonClrTypes) => EnableDynamicJson(jsonbClrTypes, jsonClrTypes); [RequiresUnreferencedCode( "The mapping of PostgreSQL records as .NET tuples requires reflection usage which is incompatible with trimming.")] [RequiresDynamicCode( "The mapping of PostgreSQL records as .NET tuples requires dynamic code usage which is incompatible with NativeAOT.")] INpgsqlTypeMapper INpgsqlTypeMapper.EnableRecordsAsTuples() => EnableRecordsAsTuples(); [RequiresUnreferencedCode( "The use of unmapped enums, ranges or multiranges requires reflection usage which is incompatible with trimming.")] [RequiresDynamicCode( "The use of unmapped enums, ranges or multiranges requires dynamic code usage which is incompatible with NativeAOT.")] INpgsqlTypeMapper INpgsqlTypeMapper.EnableUnmappedTypes() => EnableUnmappedTypes(); /// INpgsqlTypeMapper INpgsqlTypeMapper.MapEnum<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum>(string? pgName, INpgsqlNameTranslator? nameTranslator) { _userTypeMapper.MapEnum(pgName, nameTranslator); return this; } /// [RequiresDynamicCode("Calling MapEnum with a Type can require creating new generic types or methods. This may not work when AOT compiling.")] INpgsqlTypeMapper INpgsqlTypeMapper.MapEnum([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type clrType, string? pgName, INpgsqlNameTranslator? nameTranslator) { _userTypeMapper.MapEnum(clrType, pgName, nameTranslator); return this; } /// [RequiresDynamicCode("Mapping composite types involves serializing arbitrary types which can require creating new generic types or methods. This is currently unsupported with NativeAOT, vote on issue #5303 if this is important to you.")] INpgsqlTypeMapper INpgsqlTypeMapper.MapComposite<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] T>( string? pgName, INpgsqlNameTranslator? nameTranslator) { _userTypeMapper.MapComposite(typeof(T), pgName, nameTranslator); return this; } /// [RequiresDynamicCode("Mapping composite types involves serializing arbitrary types which can require creating new generic types or methods. This is currently unsupported with NativeAOT, vote on issue #5303 if this is important to you.")] INpgsqlTypeMapper INpgsqlTypeMapper.MapComposite([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] Type clrType, string? pgName, INpgsqlNameTranslator? nameTranslator) { _userTypeMapper.MapComposite(clrType, pgName, nameTranslator); return this; } }
X Tutup