Skip to content

Implement SqlConnection.GetSchemaAsync #3005

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
69 changes: 68 additions & 1 deletion doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1357,12 +1357,28 @@ For more information on working with events, see [Connection Events](https://lea
</FireInfoMessageEventOnUserErrors>
<GetSchema2>
<summary>
Returns schema information for the data source of this <see cref="T:Microsoft.Data.SqlClient.SqlConnection" />. For more information about scheme, see <see href="https://learn.microsoft.com/sql/connect/ado-net/sql-server-schema-collections">SQL Server Schema Collections</see>.
Returns schema information for the data source of this <see cref="T:Microsoft.Data.SqlClient.SqlConnection" />. For more information about schemas, see <see href="https://learn.microsoft.com/sql/connect/ado-net/sql-server-schema-collections">SQL Server Schema Collections</see>.
</summary>
<returns>
A <see cref="T:System.Data.DataTable" /> that contains schema information.
</returns>
</GetSchema2>
<GetSchemaAsync>
<param name="cancellationToken">
The cancellation token.
</param>
<summary>
An asynchronous version of <see cref="M:Microsoft.Data.SqlClient.SqlConnection.GetSchema()" />, which returns schema information for the data source of this <see cref="T:Microsoft.Data.SqlClient.SqlConnection" />. For more information about schemas, see <see href="https://learn.microsoft.com/sql/connect/ado-net/sql-server-schema-collections">SQL Server Schema Collections</see>.
</summary>
<returns>
A task representing the asynchronous operation.
</returns>
<remarks>
<para>
For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see <see href="https://learn.microsoft.com/sql/connect/ado-net/asynchronous-programming">Asynchronous Programming</see>.
</para>
</remarks>
</GetSchemaAsync>
<GetSchemaCollectionName>
<param name="collectionName">
Specifies the name of the schema to return.
Expand Down Expand Up @@ -1648,6 +1664,28 @@ For more information on working with events, see [Connection Events](https://lea
<paramref name="collectionName" /> is specified as null.
</exception>
</GetSchemaCollectionName>
<GetSchemaCollectionNameAsync>
<param name="collectionName">
Specifies the name of the schema to return.
</param>
<param name="cancellationToken">
The cancellation token.
</param>
<summary>
An asynchronous version of <see cref="M:Microsoft.Data.SqlClient.SqlConnection.GetSchema(string)" />, which returns schema information for the data source of this <see cref="T:Microsoft.Data.SqlClient.SqlConnection" /> using the specified string for the schema name.
</summary>
<returns>
A task representing the asynchronous operation.
</returns>
<remarks>
<para>
For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see <see href="https://learn.microsoft.com/sql/connect/ado-net/asynchronous-programming">Asynchronous Programming</see>.
</para>
</remarks>
<exception cref="T:System.ArgumentException">
<paramref name="collectionName" /> is specified as null.
</exception>
</GetSchemaCollectionNameAsync>
<GetSchemaCollectionNameRestrictionValues>
<param name="collectionName">
Specifies the name of the schema to return.
Expand Down Expand Up @@ -1677,6 +1715,35 @@ For more information on working with events, see [Connection Events](https://lea
</exception>
<seealso cref="M:Microsoft.Data.SqlClient.SqlConnection.GetSchema" />
</GetSchemaCollectionNameRestrictionValues>
<GetSchemaCollectionNameRestrictionValuesAsync>
<param name="collectionName">
Specifies the name of the schema to return.
</param>
<param name="restrictionValues">
A set of restriction values for the requested schema.
</param>
<param name="cancellationToken">
The cancellation token.
</param>
<summary>
An asynchronous version of <see cref="M:Microsoft.Data.SqlClient.SqlConnection.GetSchema(string, string[])" />, which returns schema information for the data source of this <see cref="T:Microsoft.Data.SqlClient.SqlConnection" /> using the specified string for the schema name and the specified string array for the restriction values.
</summary>
<returns>
A task representing the asynchronous operation.
</returns>
<remarks>
<para>
The <paramref name="restrictionValues" /> parameter can supply <i>n</i> depth of values, which are specified by the restrictions collection for a specific collection. In order to set values on a given restriction, and not set the values of other restrictions, you need to set the preceding restrictions to <see langword="null" /> and then put the appropriate value in for the restriction that you would like to specify a value for.
</para>
<para>
An example of this is the "Tables" collection. If the "Tables" collection has three restrictions--database, owner, and table name--and you want to get back only the tables associated with the owner "Carl", you need to pass in the following values: null, "Carl". If a restriction value is not passed in, the default values are used for that restriction. This is the same mapping as passing in <see langword="null" />, which is different from passing in an empty string for the parameter value. In that case, the empty string ("") is considered to be the value for the specified parameter.
</para>
</remarks>
<exception cref="T:System.ArgumentException">
<paramref name="collectionName" /> is specified as null.
</exception>
<seealso cref="M:Microsoft.Data.SqlClient.SqlConnection.GetSchema" />
</GetSchemaCollectionNameRestrictionValuesAsync>
<InfoMessage>
<summary>
Occurs when SQL Server returns a warning or informational message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,21 @@ public event Microsoft.Data.SqlClient.SqlInfoMessageEventHandler InfoMessage { a
public override System.Data.DataTable GetSchema(string collectionName) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameRestrictionValues/*'/>
public override System.Data.DataTable GetSchema(string collectionName, string[] restrictionValues) { throw null; }
#if NET
Copy link
Contributor

Choose a reason for hiding this comment

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

This whole file is NET only, so is this #if necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The file is also used to build the netstandard2.0 ref assemblies, which doesn't provide this member in the base class.

/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaAsync/*'/>
public override System.Threading.Tasks.Task<System.Data.DataTable> GetSchemaAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameAsync/*'/>
public override System.Threading.Tasks.Task<System.Data.DataTable> GetSchemaAsync(string collectionName, System.Threading.CancellationToken cancellationToken = default) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameRestrictionValuesAsync/*'/>
public override System.Threading.Tasks.Task<System.Data.DataTable> GetSchemaAsync(string collectionName, string[] restrictionValues, System.Threading.CancellationToken cancellationToken = default) { throw null; }
#else
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we missing GetSchemaTableAsync ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've removed this method from the implementation and the ref assemblies. At the moment, we get the metadata for a result set by getting the SqlDataReader.MetaData property, which synchronously waits on the network. A future PR can perform the work needed to unpick the async-over-sync here and provide a fully async implementation.

/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaAsync/*'/>
public System.Threading.Tasks.Task<System.Data.DataTable> GetSchemaAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameAsync/*'/>
public System.Threading.Tasks.Task<System.Data.DataTable> GetSchemaAsync(string collectionName, System.Threading.CancellationToken cancellationToken = default) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameRestrictionValuesAsync/*'/>
public System.Threading.Tasks.Task<System.Data.DataTable> GetSchemaAsync(string collectionName, string[] restrictionValues, System.Threading.CancellationToken cancellationToken = default) { throw null; }
#endif
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/Open/*'/>
public override void Open() { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/OpenWithOverrides/*'/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1836,19 +1836,38 @@ public override DataTable GetSchema()
return GetSchema(DbMetaDataCollectionNames.MetaDataCollections, null);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaAsync/*' />
public override Task<DataTable> GetSchemaAsync(CancellationToken cancellationToken = default)
{
return GetSchemaAsync(DbMetaDataCollectionNames.MetaDataCollections, cancellationToken);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionName/*' />
public override DataTable GetSchema(string collectionName)
{
return GetSchema(collectionName, null);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameAsync/*' />
public override Task<DataTable> GetSchemaAsync(string collectionName, CancellationToken cancellationToken = default)
{
return GetSchemaAsync(collectionName, null, cancellationToken);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameRestrictionValues/*' />
public override DataTable GetSchema(string collectionName, string[] restrictionValues)
{
SqlClientEventSource.Log.TryTraceEvent("SqlConnection.GetSchema | Info | Object Id {0}, Collection Name '{1}'", ObjectID, collectionName);
return InnerConnection.GetSchema(ConnectionFactory, PoolGroup, this, collectionName, restrictionValues);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameRestrictionValuesAsync/*' />
public override Task<DataTable> GetSchemaAsync(string collectionName, string[] restrictionValues, CancellationToken cancellationToken = default)
{
SqlClientEventSource.Log.TryTraceEvent("SqlConnection.GetSchemaAsync | Info | Object Id {0}, Collection Name '{1}'", ObjectID, collectionName);
return InnerConnection.GetSchemaAsync(ConnectionFactory, PoolGroup, this, collectionName, restrictionValues, cancellationToken);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/CanCreateBatch/*'/>
public override bool CanCreateBatch => true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,12 @@ public override void EnlistTransaction(System.Transactions.Transaction transacti
public override System.Data.DataTable GetSchema(string collectionName) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameRestrictionValues/*'/>
public override System.Data.DataTable GetSchema(string collectionName, string[] restrictionValues) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaAsync/*'/>
public System.Threading.Tasks.Task<System.Data.DataTable> GetSchemaAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameAsync/*'/>
public System.Threading.Tasks.Task<System.Data.DataTable> GetSchemaAsync(string collectionName, System.Threading.CancellationToken cancellationToken = default) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameRestrictionValuesAsync/*'/>
public System.Threading.Tasks.Task<System.Data.DataTable> GetSchemaAsync(string collectionName, string[] restrictionValues, System.Threading.CancellationToken cancellationToken = default) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/Open/*'/>
public override void Open() { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/OpenWithOverrides/*'/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1752,6 +1752,46 @@ private Task InternalOpenAsync(SqlConnectionOverrides overrides, CancellationTok
}
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchema2/*' />
public override DataTable GetSchema()
{
return GetSchema(DbMetaDataCollectionNames.MetaDataCollections, null);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaAsync/*' />
public Task<DataTable> GetSchemaAsync(CancellationToken cancellationToken = default)
{
return GetSchemaAsync(DbMetaDataCollectionNames.MetaDataCollections, cancellationToken);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionName/*' />
public override DataTable GetSchema(string collectionName)
{
return GetSchema(collectionName, null);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameAsync/*' />
public Task<DataTable> GetSchemaAsync(string collectionName, CancellationToken cancellationToken = default)
{
return GetSchemaAsync(collectionName, null, cancellationToken);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameRestrictionValues/*' />
public override DataTable GetSchema(string collectionName, string[] restrictionValues)
{
// NOTE: This is virtual because not all providers may choose to support
// returning schema data
SqlConnection.ExecutePermission.Demand();
return InnerConnection.GetSchema(ConnectionFactory, PoolGroup, this, collectionName, restrictionValues);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameRestrictionValuesAsync/*' />
public Task<DataTable> GetSchemaAsync(string collectionName, string[] restrictionValues, CancellationToken cancellationToken = default)
{
SqlConnection.ExecutePermission.Demand();
return InnerConnection.GetSchemaAsync(ConnectionFactory, PoolGroup, this, collectionName, restrictionValues, cancellationToken);
}

private class OpenAsyncRetry
{
private SqlConnection _parent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,27 +313,6 @@ internal DbMetaDataFactory GetMetaDataFactoryInternal(DbConnectionInternal inter
return GetMetaDataFactory(internalConnection);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchema2/*' />
override public DataTable GetSchema()
{
return this.GetSchema(DbMetaDataCollectionNames.MetaDataCollections, null);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionName/*' />
override public DataTable GetSchema(string collectionName)
{
return this.GetSchema(collectionName, null);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/GetSchemaCollectionNameRestrictionValues/*' />
override public DataTable GetSchema(string collectionName, string[] restrictionValues)
{
// NOTE: This is virtual because not all providers may choose to support
// returning schema data
SqlConnection.ExecutePermission.Demand();
return InnerConnection.GetSchema(ConnectionFactory, PoolGroup, this, collectionName, restrictionValues);
}

internal void NotifyWeakReference(int message)
{
InnerConnection.NotifyWeakReference(message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.Common;
using Microsoft.Data.Common.ConnectionString;
Expand Down Expand Up @@ -39,6 +40,9 @@ internal override void CloseConnection(DbConnection owningObject, DbConnectionFa
protected internal override DataTable GetSchema(DbConnectionFactory factory, DbConnectionPoolGroup poolGroup, DbConnection outerConnection, string collectionName, string[] restrictions)
=> throw ADP.ClosedConnectionError();

protected internal override Task<DataTable> GetSchemaAsync(DbConnectionFactory factory, DbConnectionPoolGroup poolGroup, DbConnection outerConnection, string collectionName, string[] restrictions, CancellationToken cancellationToken)
=> throw ADP.ClosedConnectionError();

protected override DbReferenceCollection CreateReferenceCollection() => throw ADP.ClosedConnectionError();

internal override bool TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions)
Expand Down
Loading