11using System . Data ;
2-
3- using FluentCommand . Extensions ;
2+ using System . Data . Common ;
43
54using Microsoft . Data . SqlClient ;
5+ using Microsoft . Data . SqlClient . Server ;
66
77namespace FluentCommand ;
88
@@ -13,7 +13,8 @@ public static class SqlCommandExtensions
1313{
1414 /// <summary>
1515 /// Adds a new SQL Server structured table-valued parameter with the specified <paramref name="name"/> and <paramref name="data"/>.
16- /// Converts the enumerable data to a <see cref="DataTable"/> and adds it as a parameter.
16+ /// Uses <see cref="SqlDataRecordAdapter{T}"/> internally for maximum efficiency by reusing a single record per row
17+ /// with cached metadata per type.
1718 /// </summary>
1819 /// <typeparam name="TEntity">The type of the data entities.</typeparam>
1920 /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
@@ -22,11 +23,88 @@ public static class SqlCommandExtensions
2223 /// <returns>
2324 /// The same <see cref="IDataCommand"/> instance for fluent chaining.
2425 /// </returns>
25- public static IDataCommand SqlParameter < TEntity > ( this IDataCommand dataCommand , string name , IEnumerable < TEntity > data )
26+ public static IDataCommand ParameterStructured < TEntity > ( this IDataCommand dataCommand , string name , IEnumerable < TEntity > data )
2627 where TEntity : class
2728 {
28- var dataTable = data . ToDataTable ( ) ;
29- return SqlParameter ( dataCommand , name , dataTable ) ;
29+ if ( dataCommand is null )
30+ throw new ArgumentNullException ( nameof ( dataCommand ) ) ;
31+
32+ if ( string . IsNullOrEmpty ( name ) )
33+ throw new ArgumentException ( $ "'{ nameof ( name ) } ' cannot be null or empty.", nameof ( name ) ) ;
34+
35+ if ( data is null )
36+ throw new ArgumentNullException ( nameof ( data ) ) ;
37+
38+ var records = new SqlDataRecordAdapter < TEntity > ( data ) ;
39+ return ParameterStructured ( dataCommand , name , records ) ;
40+ }
41+
42+ /// <summary>
43+ /// Adds a new SQL Server structured table-valued parameter with the specified <paramref name="name"/> and <paramref name="records"/>.
44+ /// Uses <see cref="IEnumerable{SqlDataRecord}"/> for maximum efficiency with minimal memory allocation.
45+ /// </summary>
46+ /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
47+ /// <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>
49+ /// <returns>
50+ /// The same <see cref="IDataCommand"/> instance for fluent chaining.
51+ /// </returns>
52+ /// <exception cref="InvalidOperationException">
53+ /// Thrown if the underlying command is not a SQL Server command.
54+ /// </exception>
55+ public static IDataCommand ParameterStructured ( this IDataCommand dataCommand , string name , IEnumerable < SqlDataRecord > records )
56+ {
57+ if ( dataCommand is null )
58+ throw new ArgumentNullException ( nameof ( dataCommand ) ) ;
59+
60+ if ( string . IsNullOrEmpty ( name ) )
61+ throw new ArgumentException ( $ "'{ nameof ( name ) } ' cannot be null or empty.", nameof ( name ) ) ;
62+
63+ if ( records is null )
64+ throw new ArgumentNullException ( nameof ( records ) ) ;
65+
66+ var sqlParameter = CreateSqlParameter ( dataCommand ) ;
67+
68+ sqlParameter . ParameterName = name ;
69+ sqlParameter . Value = records ;
70+ sqlParameter . Direction = ParameterDirection . Input ;
71+ sqlParameter . SqlDbType = SqlDbType . Structured ;
72+
73+ return dataCommand . Parameter ( sqlParameter ) ;
74+ }
75+
76+ /// <summary>
77+ /// Adds a new SQL Server structured table-valued parameter with the specified <paramref name="name"/> and <paramref name="dataReader"/>.
78+ /// Uses <see cref="DbDataReader"/> for streaming data to the server without materializing to a <see cref="DataTable"/>.
79+ /// </summary>
80+ /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
81+ /// <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>
83+ /// <returns>
84+ /// The same <see cref="IDataCommand"/> instance for fluent chaining.
85+ /// </returns>
86+ /// <exception cref="InvalidOperationException">
87+ /// Thrown if the underlying command is not a SQL Server command.
88+ /// </exception>
89+ public static IDataCommand ParameterStructured ( this IDataCommand dataCommand , string name , DbDataReader dataReader )
90+ {
91+ if ( dataCommand is null )
92+ throw new ArgumentNullException ( nameof ( dataCommand ) ) ;
93+
94+ if ( string . IsNullOrEmpty ( name ) )
95+ throw new ArgumentException ( $ "'{ nameof ( name ) } ' cannot be null or empty.", nameof ( name ) ) ;
96+
97+ if ( dataReader is null )
98+ throw new ArgumentNullException ( nameof ( dataReader ) ) ;
99+
100+ var sqlParameter = CreateSqlParameter ( dataCommand ) ;
101+
102+ sqlParameter . ParameterName = name ;
103+ sqlParameter . Value = dataReader ;
104+ sqlParameter . Direction = ParameterDirection . Input ;
105+ sqlParameter . SqlDbType = SqlDbType . Structured ;
106+
107+ return dataCommand . Parameter ( sqlParameter ) ;
30108 }
31109
32110 /// <summary>
@@ -41,13 +119,18 @@ public static IDataCommand SqlParameter<TEntity>(this IDataCommand dataCommand,
41119 /// <exception cref="InvalidOperationException">
42120 /// Thrown if the underlying command is not a SQL Server command.
43121 /// </exception>
44- public static IDataCommand SqlParameter ( this IDataCommand dataCommand , string name , DataTable dataTable )
122+ public static IDataCommand ParameterStructured ( this IDataCommand dataCommand , string name , DataTable dataTable )
45123 {
46- var parameter = dataCommand . Command . CreateParameter ( ) ;
47- var sqlParameter = parameter as SqlParameter ;
48- if ( sqlParameter == null )
49- throw new InvalidOperationException (
50- "SqlParameter only supported by SQL Server. Make sure DataSession was created with a valid SqlConnection." ) ;
124+ if ( dataCommand is null )
125+ throw new ArgumentNullException ( nameof ( dataCommand ) ) ;
126+
127+ if ( string . IsNullOrEmpty ( name ) )
128+ throw new ArgumentException ( $ "'{ nameof ( name ) } ' cannot be null or empty.", nameof ( name ) ) ;
129+
130+ if ( dataTable is null )
131+ throw new ArgumentNullException ( nameof ( dataTable ) ) ;
132+
133+ var sqlParameter = CreateSqlParameter ( dataCommand ) ;
51134
52135 sqlParameter . ParameterName = name ;
53136 sqlParameter . Value = dataTable ;
@@ -56,4 +139,15 @@ public static IDataCommand SqlParameter(this IDataCommand dataCommand, string na
56139
57140 return dataCommand . Parameter ( sqlParameter ) ;
58141 }
142+
143+ private static SqlParameter CreateSqlParameter ( IDataCommand dataCommand )
144+ {
145+ var parameter = dataCommand . Command . CreateParameter ( ) ;
146+ var sqlParameter = parameter as SqlParameter ;
147+ if ( sqlParameter == null )
148+ throw new InvalidOperationException (
149+ "SqlParameter only supported by SQL Server. Make sure DataSession was created with a valid SqlConnection." ) ;
150+
151+ return sqlParameter ;
152+ }
59153}
0 commit comments