Skip to content

Commit 145b5f5

Browse files
authored
Add SqlCommand.DisableOutputParameters Feature (#1041)
1 parent 00cbff1 commit 145b5f5

File tree

14 files changed

+419
-60
lines changed

14 files changed

+419
-60
lines changed

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

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,13 @@ The following console application creates updates data within the **AdventureWor
286286
<exception cref="T:System.InvalidOperationException">
287287
The
288288
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
289-
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
290-
</exception>
289+
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
290+
291+
- or -
292+
293+
<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
294+
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
295+
</exception>
291296
<exception cref="T:System.IO.IOException">
292297
An error occurred in a
293298
<see cref="T:System.IO.Stream" />
@@ -399,8 +404,13 @@ To set up this example, create a new Windows application. Put a <xref:System.Win
399404
<exception cref="T:System.InvalidOperationException">
400405
The
401406
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
402-
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
403-
</exception>
407+
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
408+
409+
- or -
410+
411+
<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
412+
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
413+
</exception>
404414
</BeginExecuteNonQuery>
405415
<BeginExecuteReader name="default">
406416
<summary>
@@ -477,8 +487,13 @@ The following console application starts the process of retrieving a data reader
477487
<exception cref="T:System.InvalidOperationException">
478488
The
479489
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
480-
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
481-
</exception>
490+
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
491+
492+
- or -
493+
494+
<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
495+
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
496+
</exception>
482497
<exception cref="T:System.IO.IOException">
483498
An error occurred in a
484499
<see cref="T:System.IO.Stream" />
@@ -584,8 +599,13 @@ This example also passes the `CommandBehavior.CloseConnection` and `CommandBehav
584599
<exception cref="T:System.InvalidOperationException">
585600
The
586601
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
587-
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
588-
</exception>
602+
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
603+
604+
- or -
605+
606+
<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
607+
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
608+
</exception>
589609
<exception cref="T:System.IO.IOException">
590610
An error occurred in a
591611
<see cref="T:System.IO.Stream" />
@@ -702,8 +722,13 @@ To set up this example, create a new Windows application. Put a <xref:System.Win
702722
<exception cref="T:System.InvalidOperationException">
703723
The
704724
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
705-
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
706-
</exception>
725+
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
726+
727+
- or -
728+
729+
<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
730+
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
731+
</exception>
707732
<exception cref="T:System.IO.IOException">
708733
An error occurred in a
709734
<see cref="T:System.IO.Stream" />
@@ -831,8 +856,13 @@ This example passes the `CommandBehavior.CloseConnection` value in the `behavior
831856
<exception cref="T:System.InvalidOperationException">
832857
The
833858
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
834-
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
835-
</exception>
859+
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
860+
861+
- or -
862+
863+
<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
864+
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
865+
</exception>
836866
<exception cref="T:System.IO.IOException">
837867
An error occurred in a
838868
<see cref="T:System.IO.Stream" />
@@ -940,6 +970,11 @@ The following console application starts the process of retrieving XML data asyn
940970
The
941971
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
942972
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
973+
974+
- or -
975+
976+
<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
977+
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
943978
</exception>
944979
<exception cref="T:System.IO.IOException">
945980
An error occurred in a
@@ -1067,8 +1102,13 @@ To set up this example, create a new Windows application. Put a <xref:System.Win
10671102
<exception cref="T:System.InvalidOperationException">
10681103
The
10691104
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
1070-
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
1071-
</exception>
1105+
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
1106+
1107+
- or -
1108+
1109+
<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
1110+
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
1111+
</exception>
10721112
<exception cref="T:System.IO.IOException">
10731113
An error occurred in a
10741114
<see cref="T:System.IO.Stream" />
@@ -1343,6 +1383,33 @@ The <xref:Microsoft.Data.SqlClient.SqlCommand.CreateParameter%2A> method is a st
13431383
To be added.
13441384
</remarks>
13451385
</Dispose>
1386+
<EnableOptimizedParameterBinding>
1387+
<summary>
1388+
Gets or sets a value indicating whether the command object should optimize parameter performance by disabling Output and InputOutput directions when submitting the command to the SQL Server.
1389+
</summary>
1390+
<value>
1391+
A value indicating whether the command object should optimize parameter performance by disabling Output and InputOuput parameter directions when submitting the command to the SQL Server.
1392+
The default is <see langword="false" />.
1393+
</value>
1394+
<remarks>
1395+
<format type="text/markdown">
1396+
<![CDATA[
1397+
## Remarks
1398+
You must set the value for this property before the command is executed for it to take effect.
1399+
1400+
When a command is submitted to the server with parameters a list of the parameter names is sent as part of the submission. The list is used on the server to match Output and InputOutput parameters to the results of the query execution so that the values can be returned to the caller. This option disables the construction and submission of the parameter name list and as a consequence disables the use of Output and InputOutput parameters. The return parameter is not affected by this option.
1401+
1402+
A command sent with this option changes the way parameters are handled on the server, because there is no need to maintain an output parameter map. The result of this change is that queries with large numbers of input parameters may execute much faster.
1403+
1404+
The fewest number of parameters where this will take effect depends on the individual situation and should be detected by measuring query duration with and without the option enabled. Any query with more than 24 parameters may show lower overall query duration. Queries with parameter counts lower than 24 are unlikely to show a difference.
1405+
1406+
> [!NOTE]
1407+
If the option is enabled and a parameter with Direction Output or InputOutput is present in the Parameters collection an InvalidOperationException will be thrown when the command is executed.
1408+
1409+
]]>
1410+
</format>
1411+
</remarks>
1412+
</EnableOptimizedParameterBinding>
13461413
<EndExecuteNonQuery name="IAsyncResult">
13471414
<param name="asyncResult">
13481415
The

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,8 @@ public SqlCommand(string cmdText, Microsoft.Data.SqlClient.SqlConnection connect
534534
[System.ComponentModel.DesignOnlyAttribute(true)]
535535
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
536536
public override bool DesignTimeVisible { get { throw null; } set { } }
537+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/EnableOptimizedParameterBinding/*'/>
538+
public bool EnableOptimizedParameterBinding { get { throw null; } set { } }
537539
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/Parameters/*'/>
538540
public new Microsoft.Data.SqlClient.SqlParameterCollection Parameters { get { throw null; } }
539541
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/Transaction/*'/>

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,9 @@ public override bool DesignTimeVisible
692692
}
693693
}
694694

695+
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/EnableOptimizedParameterBinding/*'/>
696+
public bool EnableOptimizedParameterBinding { get; set; }
697+
695698
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/Parameters/*'/>
696699
new public SqlParameterCollection Parameters
697700
{

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,11 @@ internal static Exception ParameterCannotBeEmpty(string paramName)
495495
return ADP.ArgumentNull(System.StringsHelper.GetString(Strings.SQL_ParameterCannotBeEmpty, paramName));
496496
}
497497

498+
internal static Exception ParameterDirectionInvalidForOptimizedBinding(string paramName)
499+
{
500+
return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParameterDirectionInvalidForOptimizedBinding, paramName));
501+
}
502+
498503
internal static Exception ActiveDirectoryInteractiveTimeout()
499504
{
500505
return ADP.TimeoutException(Strings.SQL_Timeout_Active_Directory_Interactive_Authentication);

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

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8988,14 +8988,19 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo
89888988
int parametersLength = rpcext.userParamCount + rpcext.systemParamCount;
89898989

89908990
bool isAdvancedTraceOn = SqlClientEventSource.Log.IsAdvancedTraceOn();
8991+
bool enableOptimizedParameterBinding = cmd.EnableOptimizedParameterBinding;
89918992

89928993
for (int i = (ii == startRpc) ? startParam : 0; i < parametersLength; i++)
89938994
{
89948995
byte options = 0;
89958996
SqlParameter param = rpcext.GetParameterByIndex(i, out options);
89968997
// Since we are reusing the parameters array, we cannot rely on length to indicate no of parameters.
89978998
if (param == null)
8999+
{
89989000
break; // End of parameters for this execute
9001+
}
9002+
9003+
ParameterDirection parameterDirection = param.Direction;
89999004

90009005
// Throw an exception if ForceColumnEncryption is set on a parameter and the ColumnEncryption is not enabled on SqlConnection or SqlCommand
90019006
if (param.ForceColumnEncryption &&
@@ -9007,12 +9012,17 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo
90079012

90089013
// Check if the applications wants to force column encryption to avoid sending sensitive data to server
90099014
if (param.ForceColumnEncryption && param.CipherMetadata == null
9010-
&& (param.Direction == ParameterDirection.Input || param.Direction == ParameterDirection.InputOutput))
9015+
&& (parameterDirection == ParameterDirection.Input || parameterDirection == ParameterDirection.InputOutput))
90119016
{
90129017
// Application wants a parameter to be encrypted before sending it to server, however server doesnt think this parameter needs encryption.
90139018
throw SQL.ParamUnExpectedEncryptionMetadata(param.ParameterName, rpcext.GetCommandTextOrRpcName());
90149019
}
90159020

9021+
if (enableOptimizedParameterBinding && (parameterDirection == ParameterDirection.Output || parameterDirection == ParameterDirection.InputOutput))
9022+
{
9023+
throw SQL.ParameterDirectionInvalidForOptimizedBinding(param.ParameterName);
9024+
}
9025+
90169026
// Validate parameters are not variable length without size and with null value.
90179027
param.Validate(i, isCommandProc);
90189028

@@ -9021,7 +9031,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo
90219031

90229032
if (mt.IsNewKatmaiType)
90239033
{
9024-
WriteSmiParameter(param, i, 0 != (options & TdsEnums.RPC_PARAM_DEFAULT), stateObj, isAdvancedTraceOn);
9034+
WriteSmiParameter(param, i, 0 != (options & TdsEnums.RPC_PARAM_DEFAULT), stateObj, enableOptimizedParameterBinding, isAdvancedTraceOn);
90259035
continue;
90269036
}
90279037

@@ -9031,7 +9041,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo
90319041
throw ADP.VersionDoesNotSupportDataType(mt.TypeName);
90329042
}
90339043

9034-
Task writeParamTask = TDSExecuteRPCAddParameter(stateObj, param, mt, options, cmd);
9044+
Task writeParamTask = TDSExecuteRPCAddParameter(stateObj, param, mt, options, cmd, enableOptimizedParameterBinding);
90359045

90369046
if (!sync)
90379047
{
@@ -9148,7 +9158,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo
91489158
}
91499159
}
91509160

9151-
private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParameter param, MetaType mt, byte options, SqlCommand command)
9161+
private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParameter param, MetaType mt, byte options, SqlCommand command, bool isAnonymous)
91529162
{
91539163
int tempLen;
91549164
object value = null;
@@ -9173,7 +9183,7 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet
91739183
}
91749184
}
91759185

9176-
WriteParameterName(param.ParameterNameFixed, stateObj);
9186+
WriteParameterName(param.ParameterNameFixed, stateObj, isAnonymous);
91779187

91789188
// Write parameter status
91799189
stateObj.WriteByte(options);
@@ -9695,11 +9705,11 @@ private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, T
96959705
}
96969706

96979707

9698-
private void WriteParameterName(string parameterName, TdsParserStateObject stateObj)
9708+
private void WriteParameterName(string parameterName, TdsParserStateObject stateObj, bool isAnonymous)
96999709
{
97009710
// paramLen
97019711
// paramName
9702-
if (!string.IsNullOrEmpty(parameterName))
9712+
if (!isAnonymous && !string.IsNullOrEmpty(parameterName))
97039713
{
97049714
Debug.Assert(parameterName.Length <= 0xff, "parameter name can only be 255 bytes, shouldn't get to TdsParser!");
97059715
int tempLen = parameterName.Length & 0xff;
@@ -9712,7 +9722,7 @@ private void WriteParameterName(string parameterName, TdsParserStateObject state
97129722
}
97139723
}
97149724

9715-
private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj, bool advancedTraceIsOn)
9725+
private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj, bool isAnonymous, bool advancedTraceIsOn)
97169726
{
97179727
//
97189728
// Determine Metadata
@@ -9771,7 +9781,7 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa
97719781
//
97729782
// Write parameter metadata
97739783
//
9774-
WriteSmiParameterMetaData(metaData, sendDefault, stateObj);
9784+
WriteSmiParameterMetaData(metaData, sendDefault, isAnonymous, stateObj);
97759785

97769786
//
97779787
// Now write the value
@@ -9790,7 +9800,7 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa
97909800
}
97919801

97929802
// Writes metadata portion of parameter stream from an SmiParameterMetaData object.
9793-
private void WriteSmiParameterMetaData(SmiParameterMetaData metaData, bool sendDefault, TdsParserStateObject stateObj)
9803+
private void WriteSmiParameterMetaData(SmiParameterMetaData metaData, bool sendDefault, bool isAnonymous, TdsParserStateObject stateObj)
97949804
{
97959805
// Determine status
97969806
byte status = 0;
@@ -9805,7 +9815,7 @@ private void WriteSmiParameterMetaData(SmiParameterMetaData metaData, bool sendD
98059815
}
98069816

98079817
// Write everything out
9808-
WriteParameterName(metaData.Name, stateObj);
9818+
WriteParameterName(metaData.Name, stateObj, isAnonymous);
98099819
stateObj.WriteByte(status);
98109820
WriteSmiTypeInfo(metaData, stateObj);
98119821
}

0 commit comments

Comments
 (0)