using Microsoft.Data.SqlClient; using System; using System.Threading; using System.Threading.Tasks; namespace XLAB2.Infrastructure; internal sealed class SqlServerConnectionFactory : IDatabaseConnectionFactory { private static readonly Lazy CurrentFactory = new(() => new SqlServerConnectionFactory(DatabaseConfiguration.Current), LazyThreadSafetyMode.ExecutionAndPublication); private readonly DatabaseOptions _options; public SqlServerConnectionFactory(DatabaseOptions options) { _options = options ?? throw new ArgumentNullException(nameof(options)); ConnectionString = BuildConnectionString(options); } public static IDatabaseConnectionFactory Current => CurrentFactory.Value; public string ConnectionString { get; } public DatabaseOptions Options => _options; public SqlConnection CreateConnection() { return new SqlConnection(ConnectionString); } public async Task OpenConnectionAsync(CancellationToken cancellationToken = default) { var connection = CreateConnection(); try { await connection.OpenAsync(cancellationToken).ConfigureAwait(false); return connection; } catch { await connection.DisposeAsync().ConfigureAwait(false); throw; } } internal static string BuildConnectionString(DatabaseOptions options) { var builder = new SqlConnectionStringBuilder { ApplicationName = string.IsNullOrWhiteSpace(options.ApplicationName) ? "XLAB2" : options.ApplicationName.Trim(), ConnectRetryCount = Math.Max(0, options.ConnectRetryCount), ConnectRetryInterval = Math.Max(1, options.ConnectRetryIntervalSeconds), ConnectTimeout = Math.Max(1, options.ConnectTimeoutSeconds), DataSource = options.Server.Trim(), Encrypt = options.Encrypt, InitialCatalog = options.Database.Trim(), IntegratedSecurity = options.IntegratedSecurity, MaxPoolSize = Math.Max(1, options.MaxPoolSize), MinPoolSize = Math.Max(0, options.MinPoolSize), MultipleActiveResultSets = options.MultipleActiveResultSets, Pooling = options.Pooling, TrustServerCertificate = options.TrustServerCertificate }; return builder.ConnectionString; } }