@@ -19,11 +19,11 @@ public static class SqlCommandExtensions
1919 /// <typeparam name="TEntity">The type of the data entities.</typeparam>
2020 /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
2121 /// <param name="name">The name of the parameter.</param>
22- /// <param name="data">The enumerable data to be added as a table-valued parameter.</param>
22+ /// <param name="data">The enumerable data to be added as a table-valued parameter. Can be <c>null</c> or empty. </param>
2323 /// <returns>
2424 /// The same <see cref="IDataCommand"/> instance for fluent chaining.
2525 /// </returns>
26- public static IDataCommand ParameterStructured < TEntity > ( this IDataCommand dataCommand , string name , IEnumerable < TEntity > data )
26+ public static IDataCommand ParameterStructured < TEntity > ( this IDataCommand dataCommand , string name , IEnumerable < TEntity > ? data )
2727 where TEntity : class
2828 {
2929 if ( dataCommand is null )
@@ -32,10 +32,7 @@ public static IDataCommand ParameterStructured<TEntity>(this IDataCommand dataCo
3232 if ( string . IsNullOrEmpty ( name ) )
3333 throw new ArgumentException ( $ "'{ nameof ( name ) } ' cannot be null or empty.", nameof ( name ) ) ;
3434
35- if ( data is null )
36- throw new ArgumentNullException ( nameof ( data ) ) ;
37-
38- var records = new SqlDataRecordAdapter < TEntity > ( data ) ;
35+ var records = data is null ? null : new SqlDataRecordAdapter < TEntity > ( data ) ;
3936 return ParameterStructured ( dataCommand , name , records ) ;
4037 }
4138
@@ -45,30 +42,29 @@ public static IDataCommand ParameterStructured<TEntity>(this IDataCommand dataCo
4542 /// </summary>
4643 /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
4744 /// <param name="name">The name of the parameter.</param>
48- /// <param name="records">The <see cref="IEnumerable{SqlDataRecord}"/> to be added as a table-valued parameter.</param>
45+ /// <param name="records">The <see cref="IEnumerable{SqlDataRecord}"/> to be added as a table-valued parameter. Can be <c>null</c> or empty. </param>
4946 /// <returns>
5047 /// The same <see cref="IDataCommand"/> instance for fluent chaining.
5148 /// </returns>
5249 /// <exception cref="InvalidOperationException">
5350 /// Thrown if the underlying command is not a SQL Server command.
5451 /// </exception>
55- public static IDataCommand ParameterStructured ( this IDataCommand dataCommand , string name , IEnumerable < SqlDataRecord > records )
52+ public static IDataCommand ParameterStructured ( this IDataCommand dataCommand , string name , IEnumerable < SqlDataRecord > ? records )
5653 {
5754 if ( dataCommand is null )
5855 throw new ArgumentNullException ( nameof ( dataCommand ) ) ;
5956
6057 if ( string . IsNullOrEmpty ( name ) )
6158 throw new ArgumentException ( $ "'{ nameof ( name ) } ' cannot be null or empty.", nameof ( name ) ) ;
6259
63- if ( records is null )
64- throw new ArgumentNullException ( nameof ( records ) ) ;
65-
6660 var sqlParameter = CreateSqlParameter ( dataCommand ) ;
6761
6862 sqlParameter . ParameterName = name ;
69- sqlParameter . Value = records ;
70- sqlParameter . Direction = ParameterDirection . Input ;
7163 sqlParameter . SqlDbType = SqlDbType . Structured ;
64+ sqlParameter . Direction = ParameterDirection . Input ;
65+
66+ // SQL Server requires null (not an empty enumerable) when a TVP has no rows
67+ sqlParameter . Value = records ? . Any ( ) == true ? records : null ;
7268
7369 return dataCommand . Parameter ( sqlParameter ) ;
7470 }
@@ -79,30 +75,29 @@ public static IDataCommand ParameterStructured(this IDataCommand dataCommand, st
7975 /// </summary>
8076 /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
8177 /// <param name="name">The name of the parameter.</param>
82- /// <param name="dataReader">The <see cref="DbDataReader"/> to be added as a table-valued parameter.</param>
78+ /// <param name="dataReader">The <see cref="DbDataReader"/> to be added as a table-valued parameter. Can be <c>null</c>. </param>
8379 /// <returns>
8480 /// The same <see cref="IDataCommand"/> instance for fluent chaining.
8581 /// </returns>
8682 /// <exception cref="InvalidOperationException">
8783 /// Thrown if the underlying command is not a SQL Server command.
8884 /// </exception>
89- public static IDataCommand ParameterStructured ( this IDataCommand dataCommand , string name , DbDataReader dataReader )
85+ public static IDataCommand ParameterStructured ( this IDataCommand dataCommand , string name , DbDataReader ? dataReader )
9086 {
9187 if ( dataCommand is null )
9288 throw new ArgumentNullException ( nameof ( dataCommand ) ) ;
9389
9490 if ( string . IsNullOrEmpty ( name ) )
9591 throw new ArgumentException ( $ "'{ nameof ( name ) } ' cannot be null or empty.", nameof ( name ) ) ;
9692
97- if ( dataReader is null )
98- throw new ArgumentNullException ( nameof ( dataReader ) ) ;
99-
10093 var sqlParameter = CreateSqlParameter ( dataCommand ) ;
10194
10295 sqlParameter . ParameterName = name ;
103- sqlParameter . Value = dataReader ;
104- sqlParameter . Direction = ParameterDirection . Input ;
10596 sqlParameter . SqlDbType = SqlDbType . Structured ;
97+ sqlParameter . Direction = ParameterDirection . Input ;
98+
99+ // SQL Server requires null (not an empty reader) when a TVP has no rows
100+ sqlParameter . Value = dataReader ? . HasRows == true ? dataReader : null ;
106101
107102 return dataCommand . Parameter ( sqlParameter ) ;
108103 }
@@ -112,30 +107,29 @@ public static IDataCommand ParameterStructured(this IDataCommand dataCommand, st
112107 /// </summary>
113108 /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
114109 /// <param name="name">The name of the parameter.</param>
115- /// <param name="dataTable">The <see cref="DataTable"/> to be added as a table-valued parameter.</param>
110+ /// <param name="dataTable">The <see cref="DataTable"/> to be added as a table-valued parameter. Can be <c>null</c> or empty. </param>
116111 /// <returns>
117112 /// The same <see cref="IDataCommand"/> instance for fluent chaining.
118113 /// </returns>
119114 /// <exception cref="InvalidOperationException">
120115 /// Thrown if the underlying command is not a SQL Server command.
121116 /// </exception>
122- public static IDataCommand ParameterStructured ( this IDataCommand dataCommand , string name , DataTable dataTable )
117+ public static IDataCommand ParameterStructured ( this IDataCommand dataCommand , string name , DataTable ? dataTable )
123118 {
124119 if ( dataCommand is null )
125120 throw new ArgumentNullException ( nameof ( dataCommand ) ) ;
126121
127122 if ( string . IsNullOrEmpty ( name ) )
128123 throw new ArgumentException ( $ "'{ nameof ( name ) } ' cannot be null or empty.", nameof ( name ) ) ;
129124
130- if ( dataTable is null )
131- throw new ArgumentNullException ( nameof ( dataTable ) ) ;
132-
133125 var sqlParameter = CreateSqlParameter ( dataCommand ) ;
134126
135127 sqlParameter . ParameterName = name ;
136- sqlParameter . Value = dataTable ;
137- sqlParameter . Direction = ParameterDirection . Input ;
138128 sqlParameter . SqlDbType = SqlDbType . Structured ;
129+ sqlParameter . Direction = ParameterDirection . Input ;
130+
131+ // SQL Server requires null (not an empty DataTable) when a TVP has no rows
132+ sqlParameter . Value = dataTable ? . Rows . Count > 0 ? dataTable : null ;
139133
140134 return dataCommand . Parameter ( sqlParameter ) ;
141135 }
0 commit comments