Skip to content

Commit 81052d6

Browse files
authored
Feature | Add "Command Timeout" connection string option (#722)
1 parent 5934db4 commit 81052d6

18 files changed

+288
-13
lines changed

doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml

+1
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,7 @@ For example, with a 30 second time out, if <xref:Microsoft.Data.SqlClient.SqlDat
12401240
[!code-csharp[SqlCommand CommandTimeout](~/../sqlclient/doc/samples/SqlCommand_CommandTimeout.cs)]
12411241
]]></format>
12421242
</remarks>
1243+
<exception cref="T:System.ArgumentException">The value set is less than 0.</exception>
12431244
</CommandTimeout>
12441245
<CommandType>
12451246
<summary>

doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml

+18
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,23 @@ The connection string contains <see langword="Context Connection=true" />.
472472
<value>The list of trusted master key paths for the column encryption.</value>
473473
<remarks>To be added.</remarks>
474474
</ColumnEncryptionTrustedMasterKeyPaths>
475+
<CommandTimeout>
476+
<summary>
477+
Gets the default wait time (in seconds) before terminating the attempt to execute a command and generating an error. The default is 30 seconds.
478+
</summary>
479+
<value>
480+
The time in seconds to wait for the command to execute. The default is 30 seconds.
481+
</value>
482+
<remarks>
483+
<format type="text/markdown"><![CDATA[
484+
485+
## Remarks
486+
487+
You can set the default wait time by using the `Command Timeout` keyword in the connection string. A value of 0 indicates no limit (an attempt to execute a command will wait indefinitely).
488+
489+
]]></format>
490+
</remarks>
491+
</CommandTimeout>
475492
<ConnectionString>
476493
<summary>Gets or sets the string used to open a SQL Server database.</summary>
477494
<value>The connection string that includes the source database name, and other parameters needed to establish the initial connection. The default value is an empty string.</value>
@@ -517,6 +534,7 @@ The connection string contains <see langword="Context Connection=true" />.
517534
|AttachDBFilename<br /><br /> -or-<br /><br /> Extended Properties<br /><br /> -or-<br /><br /> Initial File Name|N/A|The name of the primary database file, including the full path name of an attachable database. AttachDBFilename is only supported for primary data files with an .mdf extension.<br /><br /> If the value of the AttachDBFileName key is specified in the connection string, the database is attached and becomes the default database for the connection.<br /><br /> If this key is not specified and if the database was previously attached, the database will not be reattached. The previously attached database will be used as the default database for the connection.<br /><br /> If this key is specified together with the AttachDBFileName key, the value of this key will be used as the alias. However, if the name is already used in another attached database, the connection will fail.<br /><br /> The path may be absolute or relative by using the DataDirectory substitution string. If DataDirectory is used, the database file must exist within a subdirectory of the directory pointed to by the substitution string. **Note:** Remote server, HTTP, and UNC path names are not supported. <br /><br /> The database name must be specified with the keyword 'database' (or one of its aliases) as in the following:<br /><br /> <code>"AttachDbFileName=&#124;DataDirectory&#124;\data\YourDB.mdf;integrated security=true;database=YourDatabase"</code><br /><br /> An error will be generated if a log file exists in the same directory as the data file and the 'database' keyword is used when attaching the primary data file. In this case, remove the log file. Once the database is attached, a new log file will be automatically generated based on the physical path.|
518535
|Authentication|N/A|The authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities).<br /><br /> Valid values are:<br /><br /> `Active Directory Integrated`, `Active Directory Interactive`, `Active Directory Password`, `Sql Password`. Currently `Active Directory Integrated` and `Active Directory Interactive` modes of authentication are supported only for .NET Framework. |
519536
|Column Encryption Setting|N/A|Enables or disables [Always Encrypted](/sql/relational-databases/security/encryption/always-encrypted-database-engine?view=sql-server-2017) functionality for the connection.|
537+
|Command Timeout|30|The default wait time (in seconds) before terminating the attempt to execute a command and generating an error.<br /><br /> Valid values are greater than or equal to 0 and less than or equal to 2147483647.|
520538
|Connect Timeout<br /><br /> -or-<br /><br /> Connection Timeout<br /><br /> -or-<br /><br /> Timeout|15|The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error.<br /><br /> Valid values are greater than or equal to 0 and less than or equal to 2147483647.<br /><br /> When opening a connection to a Azure SQL Database, set the connection timeout to 30 seconds.|
521539
|Connection Lifetime<br /><br /> -or-<br /><br /> Load Balance Timeout|0|When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by `Connection Lifetime`. This is useful in clustered configurations to force load balancing between a running server and a server just brought online.<br /><br /> A value of zero (0) causes pooled connections to have the maximum connection timeout.|
522540
|Connect Retry Count<br /><br /> -or-<br /><br />ConnectRetryCount|1|Controls the number of reconnection attempts after the client identifies an idle connection failure. Valid values are 0 to 255. The default is 1. 0 means do not attempt to reconnect (disable connection resiliency).<br /><br /> For additional information about idle connection resiliency, see [Technical Article - Idle Connection Resiliency](https://go.microsoft.com/fwlink/?LinkId=393996).|

doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml

+17
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,23 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security
195195
<value>The column encryption settings for the connection string builder.</value>
196196
<remarks>To be added.</remarks>
197197
</ColumnEncryptionSetting>
198+
<CommandTimeout>
199+
<summary>
200+
The default wait time (in seconds) before terminating the attempt to execute a command and generating an error. The default is 30 seconds.
201+
</summary>
202+
<value>
203+
The time in seconds to wait for the command to execute. The default is 30 seconds.
204+
</value>
205+
<remarks>
206+
<format type="text/markdown"><![CDATA[
207+
208+
## Remarks
209+
This property corresponds to the "Command Timeout" key within the <xref:Microsoft.Data.SqlClient.SqlConnection> connection string.
210+
211+
]]></format>
212+
</remarks>
213+
<exception cref="T:System.ArgumentException">The value set is less than 0.</exception>
214+
</CommandTimeout>
198215
<ConnectionReset>
199216
<summary>Obsolete. Gets or sets a Boolean value that indicates whether the connection is reset when drawn from the connection pool.</summary>
200217
<value>The value of the <see cref="P:Microsoft.Data.SqlClient.SqlConnectionStringBuilder.ConnectionReset" /> property, or true if no value has been supplied.</value>

src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs

+7
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,9 @@ public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCreden
590590
internal string SQLDNSCachingSupportedStateBeforeRedirect { get { throw null; } }
591591

592592
object System.ICloneable.Clone() { throw null; }
593+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/CommandTimeout/*' />
594+
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
595+
public int CommandTimeout { get { throw null; } }
593596
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/ConnectionString/*'/>
594597
[System.ComponentModel.DefaultValueAttribute("")]
595598
[System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
@@ -712,6 +715,10 @@ public SqlConnectionStringBuilder(string connectionString) { }
712715
[System.ComponentModel.DisplayNameAttribute("Authentication")]
713716
[System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)]
714717
public Microsoft.Data.SqlClient.SqlAuthenticationMethod Authentication { get { throw null; } set { } }
718+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml' path='docs/members[@name="SqlConnectionStringBuilder"]/CommandTimeout/*'/>
719+
[System.ComponentModel.DisplayNameAttribute("Command Timeout")]
720+
[System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)]
721+
public int CommandTimeout { get { throw null; } set { } }
715722
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml' path='docs/members[@name="SqlConnectionStringBuilder"]/ConnectRetryCount/*'/>
716723
[System.ComponentModel.DisplayNameAttribute("Connect Retry Count")]
717724
[System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)]

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj
487487

488488
internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value)
489489
{
490-
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 6, "SqlAuthenticationMethod enum has changed, update needed");
490+
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 7, "SqlAuthenticationMethod enum has changed, update needed");
491491
return value == SqlAuthenticationMethod.SqlPassword
492492
|| value == SqlAuthenticationMethod.ActiveDirectoryPassword
493493
|| value == SqlAuthenticationMethod.ActiveDirectoryIntegrated
@@ -676,6 +676,7 @@ internal static partial class DbConnectionStringDefaults
676676
internal const ApplicationIntent ApplicationIntent = Microsoft.Data.SqlClient.ApplicationIntent.ReadWrite;
677677
internal const string ApplicationName = "Core Microsoft SqlClient Data Provider";
678678
internal const string AttachDBFilename = "";
679+
internal const int CommandTimeout = 30;
679680
internal const int ConnectTimeout = 15;
680681
internal const string CurrentLanguage = "";
681682
internal const string DataSource = "";
@@ -719,6 +720,7 @@ internal static partial class DbConnectionStringKeywords
719720
internal const string ApplicationName = "Application Name";
720721
internal const string AsynchronousProcessing = "Asynchronous Processing";
721722
internal const string AttachDBFilename = "AttachDbFilename";
723+
internal const string CommandTimeout = "Command Timeout";
722724
internal const string ConnectTimeout = "Connect Timeout";
723725
internal const string ConnectionReset = "Connection Reset";
724726
internal const string ContextConnection = "Context Connection";

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs

+12-4
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ protected override void AfterCleared(SqlCommand owner)
6565
}
6666

6767
private CommandType _commandType;
68-
private int _commandTimeout = ADP.DefaultCommandTimeout;
68+
private int? _commandTimeout;
6969
private UpdateRowSource _updatedRowSource = UpdateRowSource.Both;
7070
private bool _designTimeInvisible;
7171

@@ -572,7 +572,7 @@ override public int CommandTimeout
572572
{
573573
get
574574
{
575-
return _commandTimeout;
575+
return _commandTimeout ?? DefaultCommandTimeout;
576576
}
577577
set
578578
{
@@ -592,10 +592,18 @@ override public int CommandTimeout
592592
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/ResetCommandTimeout/*'/>
593593
public void ResetCommandTimeout()
594594
{
595-
if (ADP.DefaultCommandTimeout != _commandTimeout)
595+
if (ADP.DefaultCommandTimeout != CommandTimeout)
596596
{
597597
PropertyChanging();
598-
_commandTimeout = ADP.DefaultCommandTimeout;
598+
_commandTimeout = DefaultCommandTimeout;
599+
}
600+
}
601+
602+
private int DefaultCommandTimeout
603+
{
604+
get
605+
{
606+
return _activeConnection?.CommandTimeout ?? ADP.DefaultCommandTimeout;
599607
}
600608
}
601609

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs

+10
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,16 @@ public override int ConnectionTimeout
505505
}
506506
}
507507

508+
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/CommandTimeout/*' />
509+
public int CommandTimeout
510+
{
511+
get
512+
{
513+
SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
514+
return ((null != constr) ? constr.CommandTimeout : SqlConnectionString.DEFAULT.Command_Timeout);
515+
}
516+
}
517+
508518
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/AccessToken/*' />
509519
// AccessToken: To be used for token based authentication
510520
public string AccessToken

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs

+13
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ internal static partial class DEFAULT
2323
internal const ApplicationIntent ApplicationIntent = DbConnectionStringDefaults.ApplicationIntent;
2424
internal const string Application_Name = TdsEnums.SQL_PROVIDER_NAME;
2525
internal const string AttachDBFilename = "";
26+
internal const int Command_Timeout = ADP.DefaultCommandTimeout;
2627
internal const int Connect_Timeout = ADP.DefaultConnectionTimeout;
2728
internal const string Current_Language = "";
2829
internal const string Data_Source = "";
@@ -67,6 +68,8 @@ internal static class KEY
6768
internal const string ColumnEncryptionSetting = "column encryption setting";
6869
internal const string EnclaveAttestationUrl = "enclave attestation url";
6970
internal const string AttestationProtocol = "attestation protocol";
71+
72+
internal const string Command_Timeout = "command timeout";
7073
internal const string Connect_Timeout = "connect timeout";
7174
internal const string Connection_Reset = "connection reset";
7275
internal const string Context_Connection = "context connection";
@@ -210,6 +213,7 @@ internal static class TRANSACTIONBINDING
210213
private readonly string _enclaveAttestationUrl;
211214
private readonly SqlConnectionAttestationProtocol _attestationProtocol;
212215

216+
private readonly int _commandTimeout;
213217
private readonly int _connectTimeout;
214218
private readonly int _loadBalanceTimeout;
215219
private readonly int _maxPoolSize;
@@ -265,6 +269,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G
265269
_userInstance = ConvertValueToBoolean(KEY.User_Instance, DEFAULT.User_Instance);
266270
_multiSubnetFailover = ConvertValueToBoolean(KEY.MultiSubnetFailover, DEFAULT.MultiSubnetFailover);
267271

272+
_commandTimeout = ConvertValueToInt32(KEY.Command_Timeout, DEFAULT.Command_Timeout);
268273
_connectTimeout = ConvertValueToInt32(KEY.Connect_Timeout, DEFAULT.Connect_Timeout);
269274
_loadBalanceTimeout = ConvertValueToInt32(KEY.Load_Balance_Timeout, DEFAULT.Load_Balance_Timeout);
270275
_maxPoolSize = ConvertValueToInt32(KEY.Max_Pool_Size, DEFAULT.Max_Pool_Size);
@@ -307,6 +312,11 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G
307312
throw ADP.InvalidConnectionOptionValue(KEY.Connect_Timeout);
308313
}
309314

315+
if (_commandTimeout < 0)
316+
{
317+
throw ADP.InvalidConnectionOptionValue(KEY.Command_Timeout);
318+
}
319+
310320
if (_maxPoolSize < 1)
311321
{
312322
throw ADP.InvalidConnectionOptionValue(KEY.Max_Pool_Size);
@@ -490,6 +500,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS
490500
_pooling = connectionOptions._pooling;
491501
_replication = connectionOptions._replication;
492502
_userInstance = userInstance;
503+
_commandTimeout = connectionOptions._commandTimeout;
493504
_connectTimeout = connectionOptions._connectTimeout;
494505
_loadBalanceTimeout = connectionOptions._loadBalanceTimeout;
495506
#if netcoreapp
@@ -545,6 +556,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS
545556
internal bool Replication { get { return _replication; } }
546557
internal bool UserInstance { get { return _userInstance; } }
547558

559+
internal int CommandTimeout { get { return _commandTimeout; } }
548560
internal int ConnectTimeout { get { return _connectTimeout; } }
549561
internal int LoadBalanceTimeout { get { return _loadBalanceTimeout; } }
550562
internal int MaxPoolSize { get { return _maxPoolSize; } }
@@ -633,6 +645,7 @@ internal static Dictionary<string, string> GetParseSynonyms()
633645
#if netcoreapp
634646
{ KEY.PoolBlockingPeriod, KEY.PoolBlockingPeriod},
635647
#endif
648+
{ KEY.Command_Timeout, KEY.Command_Timeout },
636649
{ KEY.Connect_Timeout, KEY.Connect_Timeout },
637650
{ KEY.Connection_Reset, KEY.Connection_Reset },
638651
{ KEY.Context_Connection, KEY.Context_Connection },

0 commit comments

Comments
 (0)