• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loresoft / FluentCommand / 23307015362

19 Mar 2026 05:01PM UTC coverage: 57.452% (+0.05%) from 57.402%
23307015362

push

github

pwelter34
Support null/empty TVP parameters

1412 of 3077 branches covered (45.89%)

Branch coverage included in aggregate %.

8 of 11 new or added lines in 2 files covered. (72.73%)

4289 of 6846 relevant lines covered (62.65%)

331.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.41
/src/FluentCommand.SqlServer/SqlCommandExtensions.cs
1
using System.Data;
2
using System.Data.Common;
3

4
using Microsoft.Data.SqlClient;
5
using Microsoft.Data.SqlClient.Server;
6

7
namespace FluentCommand;
8

9
/// <summary>
10
/// Extension methods for <see cref="IDataCommand"/> specific to SQL Server.
11
/// </summary>
12
public static class SqlCommandExtensions
13
{
14
    /// <summary>
15
    /// Adds a new SQL Server structured table-valued parameter with the specified <paramref name="name"/> and <paramref name="data"/>.
16
    /// Uses <see cref="SqlDataRecordAdapter{T}"/> internally for maximum efficiency by reusing a single record per row
17
    /// with cached metadata per type.
18
    /// </summary>
19
    /// <typeparam name="TEntity">The type of the data entities.</typeparam>
20
    /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
21
    /// <param name="name">The name of the 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>
23
    /// <returns>
24
    /// The same <see cref="IDataCommand"/> instance for fluent chaining.
25
    /// </returns>
26
    public static IDataCommand ParameterStructured<TEntity>(this IDataCommand dataCommand, string name, IEnumerable<TEntity>? data)
27
        where TEntity : class
28
    {
29
        if (dataCommand is null)
5!
30
            throw new ArgumentNullException(nameof(dataCommand));
×
31

32
        if (string.IsNullOrEmpty(name))
5!
33
            throw new ArgumentException($"'{nameof(name)}' cannot be null or empty.", nameof(name));
×
34

35
        var records = data is null ? null : new SqlDataRecordAdapter<TEntity>(data);
5✔
36
        return ParameterStructured(dataCommand, name, records);
5✔
37
    }
38

39
    /// <summary>
40
    /// Adds a new SQL Server structured table-valued parameter with the specified <paramref name="name"/> and <paramref name="records"/>.
41
    /// Uses <see cref="IEnumerable{SqlDataRecord}"/> for maximum efficiency with minimal memory allocation.
42
    /// </summary>
43
    /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
44
    /// <param name="name">The name of the 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>
46
    /// <returns>
47
    /// The same <see cref="IDataCommand"/> instance for fluent chaining.
48
    /// </returns>
49
    /// <exception cref="InvalidOperationException">
50
    /// Thrown if the underlying command is not a SQL Server command.
51
    /// </exception>
52
    public static IDataCommand ParameterStructured(this IDataCommand dataCommand, string name, IEnumerable<SqlDataRecord>? records)
53
    {
54
        if (dataCommand is null)
5!
55
            throw new ArgumentNullException(nameof(dataCommand));
×
56

57
        if (string.IsNullOrEmpty(name))
5!
58
            throw new ArgumentException($"'{nameof(name)}' cannot be null or empty.", nameof(name));
×
59

60
        var sqlParameter = CreateSqlParameter(dataCommand);
5✔
61

62
        sqlParameter.ParameterName = name;
5✔
63
        sqlParameter.SqlDbType = SqlDbType.Structured;
5✔
64
        sqlParameter.Direction = ParameterDirection.Input;
5✔
65

66
        // SQL Server requires null (not an empty enumerable) when a TVP has no rows
67
        sqlParameter.Value = records?.Any() == true ? records : null;
5✔
68

69
        return dataCommand.Parameter(sqlParameter);
5✔
70
    }
71

72
    /// <summary>
73
    /// Adds a new SQL Server structured table-valued parameter with the specified <paramref name="name"/> and <paramref name="dataReader"/>.
74
    /// Uses <see cref="DbDataReader"/> for streaming data to the server without materializing to a <see cref="DataTable"/>.
75
    /// </summary>
76
    /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
77
    /// <param name="name">The name of the parameter.</param>
78
    /// <param name="dataReader">The <see cref="DbDataReader"/> to be added as a table-valued parameter. Can be <c>null</c>.</param>
79
    /// <returns>
80
    /// The same <see cref="IDataCommand"/> instance for fluent chaining.
81
    /// </returns>
82
    /// <exception cref="InvalidOperationException">
83
    /// Thrown if the underlying command is not a SQL Server command.
84
    /// </exception>
85
    public static IDataCommand ParameterStructured(this IDataCommand dataCommand, string name, DbDataReader? dataReader)
86
    {
87
        if (dataCommand is null)
×
88
            throw new ArgumentNullException(nameof(dataCommand));
×
89

90
        if (string.IsNullOrEmpty(name))
×
91
            throw new ArgumentException($"'{nameof(name)}' cannot be null or empty.", nameof(name));
×
92

93
        var sqlParameter = CreateSqlParameter(dataCommand);
×
94

95
        sqlParameter.ParameterName = name;
×
96
        sqlParameter.SqlDbType = SqlDbType.Structured;
×
NEW
97
        sqlParameter.Direction = ParameterDirection.Input;
×
98

99
        // SQL Server requires null (not an empty reader) when a TVP has no rows
NEW
100
        sqlParameter.Value = dataReader?.HasRows == true ? dataReader : null;
×
101

102
        return dataCommand.Parameter(sqlParameter);
×
103
    }
104

105
    /// <summary>
106
    /// Adds a new SQL Server structured table-valued parameter with the specified <paramref name="name"/> and <paramref name="dataTable"/>.
107
    /// </summary>
108
    /// <param name="dataCommand">The <see cref="IDataCommand"/> to extend.</param>
109
    /// <param name="name">The name of the 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>
111
    /// <returns>
112
    /// The same <see cref="IDataCommand"/> instance for fluent chaining.
113
    /// </returns>
114
    /// <exception cref="InvalidOperationException">
115
    /// Thrown if the underlying command is not a SQL Server command.
116
    /// </exception>
117
    public static IDataCommand ParameterStructured(this IDataCommand dataCommand, string name, DataTable? dataTable)
118
    {
119
        if (dataCommand is null)
3!
120
            throw new ArgumentNullException(nameof(dataCommand));
×
121

122
        if (string.IsNullOrEmpty(name))
3!
123
            throw new ArgumentException($"'{nameof(name)}' cannot be null or empty.", nameof(name));
×
124

125
        var sqlParameter = CreateSqlParameter(dataCommand);
3✔
126

127
        sqlParameter.ParameterName = name;
3✔
128
        sqlParameter.SqlDbType = SqlDbType.Structured;
3✔
129
        sqlParameter.Direction = ParameterDirection.Input;
3✔
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;
3✔
133

134
        return dataCommand.Parameter(sqlParameter);
3✔
135
    }
136

137
    private static SqlParameter CreateSqlParameter(IDataCommand dataCommand)
138
    {
139
        var parameter = dataCommand.Command.CreateParameter();
8✔
140
        var sqlParameter = parameter as SqlParameter;
8✔
141
        if (sqlParameter == null)
8!
142
            throw new InvalidOperationException(
×
143
                "SqlParameter only supported by SQL Server.  Make sure DataSession was created with a valid SqlConnection.");
×
144

145
        return sqlParameter;
8✔
146
    }
147
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc