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

loresoft / FluentCommand / 26923300515

04 Jun 2026 01:03AM UTC coverage: 65.014% (+9.9%) from 55.157%
26923300515

push

github

pwelter34
Merge branch 'master' of https://github.com/loresoft/FluentCommand

1728 of 3450 branches covered (50.09%)

Branch coverage included in aggregate %.

5510 of 7683 relevant lines covered (71.72%)

297.61 hits per line

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

58.43
/src/FluentCommand/QueryMultipleResult.cs
1
using System.Data;
2
using System.Data.Common;
3
using System.Text.Json;
4

5
using FluentCommand.Extensions;
6

7
namespace FluentCommand;
8

9
/// <summary>
10
/// Query class to wrap multiple results.
11
/// </summary>
12
internal class QueryMultipleResult : DisposableBase, IDataQuery, IDataQueryAsync
13
{
14
    private readonly DbDataReader _reader;
15
    private readonly IDataReader _dataRecord;
16
    private int _readCount;
17

18
    /// <summary>
19
    /// Initializes a new instance of the <see cref="QueryMultipleResult"/> class.
20
    /// </summary>
21
    /// <param name="reader">The reader.</param>
22
    /// <param name="jsonSerializerOptions">The JSON serializer options used by generated JSON column readers.</param>
23
    internal QueryMultipleResult(DbDataReader reader, JsonSerializerOptions? jsonSerializerOptions = null)
6✔
24
    {
25
        _readCount = 0;
6✔
26
        _reader = reader;
6✔
27
        _dataRecord = jsonSerializerOptions is null
6!
28
            ? reader
6✔
29
            : new ContextDataReader(reader, jsonSerializerOptions);
6✔
30
    }
6✔
31

32
    /// <summary>
33
    /// Executes the command against the connection and converts the results to <typeparamref name="TEntity" /> objects.
34
    /// </summary>
35
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
36
    /// <param name="factory">The <see langword="delegate" /> factory to convert the <see cref="T:System.Data.IDataReader" /> to <typeparamref name="TEntity" />.</param>
37
    /// <param name="commandBehavior">Provides a description of the results of the query and its effect on the database.</param>
38
    /// <returns>
39
    /// An <see cref="T:System.Collections.Generic.IEnumerable`1" /> of <typeparamref name="TEntity" /> objects.
40
    /// </returns>
41
    public IEnumerable<TEntity> Query<TEntity>(
42
        Func<IDataReader, TEntity> factory,
43
        CommandBehavior commandBehavior = CommandBehavior.Default)
44
    {
45
        NextResult();
6✔
46

47
        var results = new List<TEntity>();
6✔
48
        while (_reader.Read())
24✔
49
        {
50
            var entity = factory(_dataRecord);
18✔
51
            results.Add(entity);
18✔
52
        }
53

54
        return results;
6✔
55
    }
56

57
    /// <summary>
58
    /// Executes the command against the connection and converts the results to <typeparamref name="TEntity" /> objects asynchronously.
59
    /// </summary>
60
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
61
    /// <param name="factory">The <see langword="delegate" /> factory to convert the <see cref="T:System.Data.IDataReader" /> to <typeparamref name="TEntity" />.</param>
62
    /// <param name="commandBehavior">Provides a description of the results of the query and its effect on the database.</param>
63
    /// <param name="cancellationToken">The cancellation instruction.</param>
64
    /// <returns>
65
    /// An <see cref="T:System.Collections.Generic.IEnumerable`1" /> of <typeparamref name="TEntity" /> objects.
66
    /// </returns>
67
    public async Task<IEnumerable<TEntity>> QueryAsync<TEntity>(
68
        Func<IDataReader, TEntity> factory,
69
        CommandBehavior commandBehavior = CommandBehavior.Default,
70
        CancellationToken cancellationToken = default)
71
    {
72
        await NextResultAsync(cancellationToken).ConfigureAwait(false);
6✔
73

74
        var results = new List<TEntity>();
6✔
75
        while (await _reader.ReadAsync(cancellationToken).ConfigureAwait(false))
24✔
76
        {
77
            var entity = factory(_dataRecord);
18✔
78
            results.Add(entity);
18✔
79
        }
80

81
        return results;
6✔
82
    }
6✔
83

84

85
    /// <summary>
86
    /// Executes the query and returns the first row in the result as a <typeparamref name="TEntity" /> object.
87
    /// </summary>
88
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
89
    /// <param name="factory">The <see langword="delegate" /> factory to convert the <see cref="T:System.Data.IDataReader" /> to <typeparamref name="TEntity" />.</param>
90
    /// <param name="commandBehavior">Provides a description of the results of the query and its effect on the database.</param>
91
    /// <returns>
92
    /// A instance of <typeparamref name="TEntity" /> if row exists; otherwise null.
93
    /// </returns>
94
    public TEntity? QuerySingle<TEntity>(
95
        Func<IDataReader, TEntity> factory,
96
        CommandBehavior commandBehavior = CommandBehavior.Default)
97
    {
98
        NextResult();
3✔
99

100
        return _reader.Read()
3!
101
            ? factory(_dataRecord)
3✔
102
            : default;
3✔
103
    }
104

105
    /// <summary>
106
    /// Executes the query and returns the first row in the result as a <typeparamref name="TEntity" /> object asynchronously.
107
    /// </summary>
108
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
109
    /// <param name="factory">The <see langword="delegate" /> factory to convert the <see cref="T:System.Data.IDataReader" /> to <typeparamref name="TEntity" />.</param>
110
    /// <param name="commandBehavior">Provides a description of the results of the query and its effect on the database.</param>
111
    /// <param name="cancellationToken">The cancellation instruction.</param>
112
    /// <returns>
113
    /// A instance of <typeparamref name="TEntity" /> if row exists; otherwise null.
114
    /// </returns>
115
    public async Task<TEntity?> QuerySingleAsync<TEntity>(
116
        Func<IDataReader, TEntity> factory,
117
        CommandBehavior commandBehavior = CommandBehavior.Default,
118
        CancellationToken cancellationToken = default)
119
    {
120
        await NextResultAsync(cancellationToken).ConfigureAwait(false);
3✔
121

122
        return await _reader.ReadAsync(cancellationToken).ConfigureAwait(false)
3!
123
            ? factory(_dataRecord)
3✔
124
            : default;
3✔
125
    }
3✔
126

127

128
    /// <summary>
129
    /// Executes the query and returns the first column of the first row in the result set returned by the query. All other columns and rows are ignored.
130
    /// </summary>
131
    /// <typeparam name="TValue">The type of the value.</typeparam>
132
    /// <param name="convert">The <see langword="delegate" /> to convert the value..</param>
133
    /// <returns>
134
    /// The value of the first column of the first row in the result set.
135
    /// </returns>
136
    public TValue? QueryValue<TValue>(Func<object?, TValue?>? convert)
137
    {
138
        NextResult();
×
139

140
        var result = _reader.Read()
×
141
            ? _reader.GetValue(0)
×
142
            : default(TValue);
×
143

144
        return ConvertExtensions.ConvertValue(result, convert);
×
145
    }
146

147
    /// <summary>
148
    /// Executes the query and returns the first column of the first row in the result set returned by the query asynchronously. All other columns and rows are ignored.
149
    /// </summary>
150
    /// <typeparam name="TValue">The type of the value.</typeparam>
151
    /// <param name="convert">The <see langword="delegate" /> to convert the value..</param>
152
    /// <param name="cancellationToken">The cancellation instruction.</param>
153
    /// <returns>
154
    /// The value of the first column of the first row in the result set.
155
    /// </returns>
156
    public async Task<TValue?> QueryValueAsync<TValue>(
157
        Func<object?, TValue?>? convert,
158
        CancellationToken cancellationToken = default)
159
    {
160
        await NextResultAsync(cancellationToken).ConfigureAwait(false);
×
161

162
        var result = await _reader.ReadAsync(cancellationToken).ConfigureAwait(false)
×
163
            ? _reader.GetValue(0)
×
164
            : default(TValue);
×
165

166
        return ConvertExtensions.ConvertValue(result, convert);
×
167
    }
×
168

169

170
    /// <summary>
171
    /// Executes the command against the connection and converts the results to a <see cref="DataTable" />.
172
    /// </summary>
173
    /// <returns>
174
    /// A <see cref="DataTable" /> of the results.
175
    /// </returns>
176
    public DataTable QueryTable()
177
    {
178
        NextResult();
×
179

180
        var dataTable = new DataTable();
×
181
        dataTable.Load(_reader);
×
182

183
        return dataTable;
×
184
    }
185

186

187
    /// <summary>
188
    /// Executes the command against the connection and converts the results to a <see cref="DataTable" /> asynchronously.
189
    /// </summary>
190
    /// <param name="cancellationToken">The cancellation instruction.</param>
191
    /// <returns>
192
    /// A <see cref="DataTable" /> of the results.
193
    /// </returns>
194
    public async Task<DataTable> QueryTableAsync(CancellationToken cancellationToken = default)
195
    {
196
        await NextResultAsync(cancellationToken).ConfigureAwait(false);
×
197

198
        var dataTable = new DataTable();
×
199
        dataTable.Load(_reader);
×
200

201
        return dataTable;
×
202
    }
×
203

204
    /// <summary>
205
    /// Executes the command against the connection and sends the resulting <see cref="IDataReader" /> to the readAction delegate.
206
    /// </summary>
207
    /// <param name="readAction">The read action delegate to pass the open <see cref="IDataReader" />.</param>
208
    /// <param name="commandBehavior">Provides a description of the results of the query and its effect on the database.</param>
209
    /// <exception cref="System.NotImplementedException"></exception>
210
    public void Read(
211
        Action<IDataReader> readAction,
212
        CommandBehavior commandBehavior = CommandBehavior.Default)
213
    {
214
        NextResult();
×
215

216
        readAction(_dataRecord);
×
217
    }
×
218

219
    /// <summary>
220
    /// Executes the command against the connection and sends the resulting <see cref="IDataReader" /> to the readAction delegate.
221
    /// </summary>
222
    /// <param name="readAction">The read action delegate to pass the open <see cref="IDataReader" />.</param>
223
    /// <param name="commandBehavior">Provides a description of the results of the query and its effect on the database.</param>
224
    /// <param name="cancellationToken">The cancellation instruction.</param>
225
    /// <returns></returns>
226
    /// <exception cref="System.NotImplementedException"></exception>
227
    public async Task ReadAsync(
228
        Func<IDataReader, CancellationToken, Task> readAction,
229
        CommandBehavior commandBehavior = CommandBehavior.Default,
230
        CancellationToken cancellationToken = default)
231
    {
232
        await NextResultAsync(cancellationToken);
×
233

234
        await readAction(_dataRecord, cancellationToken);
×
235
    }
×
236

237

238
    private void NextResult()
239
    {
240
        if (_readCount > 0)
9✔
241
        {
242
            bool hasNextResult = _reader.NextResult();
6✔
243
            if (!hasNextResult)
6!
244
                throw new InvalidOperationException("The data reader could not advance to the next result.");
×
245
        }
246

247
        _readCount++;
9✔
248
    }
9✔
249

250
    private async Task NextResultAsync(CancellationToken cancellationToken)
251
    {
252
        if (_readCount > 0)
9✔
253
        {
254
            bool hasNextResult = await _reader.NextResultAsync(cancellationToken).ConfigureAwait(false);
6✔
255
            if (!hasNextResult)
6!
256
                throw new InvalidOperationException("The data reader could not advance to the next result.");
×
257
        }
258

259
        _readCount++;
9✔
260
    }
9✔
261

262
}
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