Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,7 @@
<Compile Include="Microsoft\Data\SqlClientX\Handlers\Connection\PreloginSubHandlers\TlsAuthenticator.cs" />
<Compile Include="Microsoft\Data\SqlClientX\Handlers\DataSourceParsingHandler.cs" />
<Compile Include="Microsoft\Data\SqlClientX\UnpooledDataSource.cs" />
<Compile Include="Microsoft\Data\SqlClientX\PoolingDataSource.cs" />
<Compile Include="Microsoft\Data\SqlClientX\SqlDataSource.cs" />
<Compile Include="Microsoft\Data\SqlClientX\SqlConnector.cs" />
<Compile Include="Microsoft\Data\SqlClientX\Handlers\SqlUtilsX.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if NET8_0_OR_GREATER

using System;
using System.Collections.Concurrent;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.Data.ProviderBase;
using Microsoft.Data.SqlClient;

namespace Microsoft.Data.SqlClientX
{
/// <summary>
/// A pooling implementation of SqlDataSource. Connections are recycled upon return to be reused by another operation.
/// </summary>
internal sealed class PoolingDataSource : SqlDataSource
{
private DbConnectionPoolGroupOptions _connectionPoolGroupOptions;

internal int MinPoolSize => _connectionPoolGroupOptions.MinPoolSize;
internal int MaxPoolSize => _connectionPoolGroupOptions.MaxPoolSize;

/// <summary>
/// Initializes a new PoolingDataSource.
/// </summary>
//TODO: support auth contexts and provider info
PoolingDataSource(SqlConnectionStringBuilder connectionStringBuilder,
SqlCredential credential,
RemoteCertificateValidationCallback userCertificateValidationCallback,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do these callbacks exist?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a holdover from npgsql inspiration. I can clean them up and come back around later as the auth flows are fleshed out.

Action<X509CertificateCollection> clientCertificatesCallback,
DbConnectionPoolGroupOptions options) : base(connectionStringBuilder, credential, userCertificateValidationCallback, clientCertificatesCallback)
{
_connectionPoolGroupOptions = options;
//TODO: other construction
}

/// <inheritdoc/>
internal override ValueTask<SqlConnector> GetInternalConnection(SqlConnectionX owningConnection, TimeSpan timeout, bool async, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

/// <inheritdoc/>
internal override ValueTask<SqlConnector> OpenNewInternalConnection(SqlConnectionX owningConnection, TimeSpan timeout, bool async, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

/// <inheritdoc/>
internal override ValueTask ReturnInternalConnection(bool async, SqlConnector connection)
{
throw new NotImplementedException();
}

/// <summary>
/// Closes extra idle connections.
/// </summary>
/// <exception cref="NotImplementedException"></exception>
internal void PruneIdleConnections()
{
throw new NotImplementedException();
}

/// <summary>
/// Warms up the pool to bring it up to min pool size.
/// </summary>
/// <exception cref="NotImplementedException"></exception>
internal void WarmUp()
{
throw new NotImplementedException();
}
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal sealed class SqlConnectionX : DbConnection, ICloneable
{
private SqlCredential? _credential;
private SqlDataSource? _dataSource;
private SqlConnector? _connection;
private SqlConnector? _internalConnection;

//TODO: Investigate if we can just use dataSource.ConnectionString. Do this when this class can resolve its own data source.
private string _connectionString = string.Empty;
Expand Down Expand Up @@ -184,13 +184,20 @@ internal Task Close(bool async)

async Task CloseAsync(bool async)
{
Debug.Assert(_connection != null);
var internalConnection = _connection;
Debug.Assert(_internalConnection != null);
Debug.Assert(_dataSource != null);

//TODO: cancel outstanding operations on connection before close
await internalConnection.Close(async).ConfigureAwait(false);
var internalConnection = _internalConnection;

//TODO: remove the internal connection's reference to this wrapper connection
if (internalConnection != null)
{
//TODO: cancel outstanding operations on connection before close
//TODO: if pooling, reset the connector

internalConnection.OwningConnection = null;
Comment thread
mdaigle marked this conversation as resolved.
Resolved
await internalConnection.Return(async).ConfigureAwait(false);
_internalConnection = null;
}

_connectionState = ConnectionState.Closed;
}
Expand Down Expand Up @@ -245,7 +252,7 @@ internal async Task Open(bool async, CancellationToken cancellationToken)
throw ADP.NoConnectionString();
}

_connection = await _dataSource.GetInternalConnection(this, TimeSpan.FromSeconds(ConnectionTimeout), async, cancellationToken).ConfigureAwait(false);
_internalConnection = await _dataSource.GetInternalConnection(this, TimeSpan.FromSeconds(ConnectionTimeout), async, cancellationToken).ConfigureAwait(false);
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,36 @@
using System.Threading;
using System.Threading.Tasks;

#nullable enable

namespace Microsoft.Data.SqlClientX
{
/// <summary>
/// Represents a physical connection with the database.
/// </summary>
internal class SqlConnector
{
private WeakReference<SqlConnectionX> _owningConnection;
internal SqlConnectionX? OwningConnection { get; set; }

/// <summary>
/// The datasource accessed by this connector.
/// The data source that generated this connector.
/// </summary>
public string DataSource => throw new NotImplementedException();
internal SqlDataSource DataSource { get; }

/// <summary>
/// The server version this connector is connected to.
/// </summary>
public string ServerVersion => throw new NotImplementedException();
internal string ServerVersion => throw new NotImplementedException();

/// <summary>
/// Represents the current state of this connection.
/// </summary>
public ConnectionState State => throw new NotImplementedException();
internal ConnectionState State => throw new NotImplementedException();

internal SqlConnector(SqlConnectionX owningConnection)
internal SqlConnector(SqlConnectionX owningConnection, SqlDataSource dataSource)
{
_owningConnection = new WeakReference<SqlConnectionX>(owningConnection);
OwningConnection = owningConnection;
DataSource = dataSource;
}

/// <summary>
Expand All @@ -44,7 +48,7 @@ internal SqlConnector(SqlConnectionX owningConnection)
/// <param name = "async" > Whether this method should run asynchronously.</param>
/// <returns>A Task indicating the result of the operation.</returns>
/// <exception cref="NotImplementedException"></exception>
public Task Close(bool async)
internal ValueTask Close(bool async)
{
throw new NotImplementedException();
}
Expand All @@ -57,10 +61,17 @@ public Task Close(bool async)
/// <param name = "cancellationToken">The token used to cancel an ongoing asynchronous call.</param>
/// <returns>A Task indicating the result of the operation.</returns>
/// <exception cref="NotImplementedException"></exception>
public Task Open(TimeSpan timeout, bool async, CancellationToken cancellationToken)
internal ValueTask Open(TimeSpan timeout, bool async, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

/// <summary>
/// Returns this connection to the data source that generated it.
/// </summary>
/// <param name="async">Whether this method should run asynchronously.</param>
/// <returns></returns>
internal ValueTask Return(bool async) => DataSource.ReturnInternalConnection(async, this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,23 @@ internal SqlDataSource(
/// <param name="cancellationToken">Cancels an outstanding asynchronous operation.</param>
/// <returns></returns>
internal abstract ValueTask<SqlConnector> GetInternalConnection(SqlConnectionX owningConnection, TimeSpan timeout, bool async, CancellationToken cancellationToken);

/// <summary>
/// Returns a SqlConnector to the data source for recycling or finalization.
/// </summary>
/// <param name="async">Whether this method should be run asynchronously.</param>
/// <param name="connection">The connection returned to the data source.</param>
internal abstract ValueTask ReturnInternalConnection(bool async, SqlConnector connection);

/// <summary>
/// Opens a new SqlConnector.
/// </summary>
/// <param name="owningConnection">The SqlConnectionX object that will exclusively own and use this connector.</param>
/// <param name="timeout">The connection timeout for this operation.</param>
/// <param name="async">Whether this method should be run asynchronously.</param>
/// <param name="cancellationToken">Cancels an outstanding asynchronous operation.</param>
/// <returns></returns>
internal abstract ValueTask<SqlConnector> OpenNewInternalConnection(SqlConnectionX owningConnection, TimeSpan timeout, bool async, CancellationToken cancellationToken);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,26 @@ internal UnpooledDataSource(
{
}

/// <summary>
/// Creates and opens a new SqlConnector.
/// </summary>
/// <param name="owningConnection">The SqlConnectionX object that will exclusively own and use this connector.</param>
/// <param name="timeout">The connection timeout for this operation.</param>
/// <param name="async">Whether this method should be run asynchronously.</param>
/// <param name="cancellationToken">Cancels an outstanding asynchronous operation.</param>
/// <returns></returns>
internal override async ValueTask<SqlConnector> GetInternalConnection(SqlConnectionX owningConnection, TimeSpan timeout, bool async, CancellationToken cancellationToken)

/// <inheritdoc/>
internal override ValueTask<SqlConnector> GetInternalConnection(SqlConnectionX owningConnection, TimeSpan timeout, bool async, CancellationToken cancellationToken)
{
var connector = new SqlConnector(owningConnection);
return OpenNewInternalConnection(owningConnection, timeout, async, cancellationToken);
}

/// <inheritdoc/>
internal override async ValueTask<SqlConnector> OpenNewInternalConnection(SqlConnectionX owningConnection, TimeSpan timeout, bool async, CancellationToken cancellationToken)
{
var connector = new SqlConnector(owningConnection, this);
await connector.Open(timeout, async, cancellationToken).ConfigureAwait(false);
return connector;
}

/// <inheritdoc/>
internal override async ValueTask ReturnInternalConnection(bool async, SqlConnector connection)
{
await connection.Close(async).ConfigureAwait(false);
}
}
}

Expand Down