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

loresoft / FluentCommand / 22970781217

11 Mar 2026 07:32PM UTC coverage: 56.585% (+0.2%) from 56.372%
22970781217

push

github

pwelter34
Add ParameterJson overloads

1277 of 2823 branches covered (45.24%)

Branch coverage included in aggregate %.

0 of 4 new or added lines in 1 file covered. (0.0%)

64 existing lines in 4 files now uncovered.

3892 of 6312 relevant lines covered (61.66%)

367.61 hits per line

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

78.74
/src/FluentCommand.Json/JsonCommandExtensions.cs
1
using System.Buffers;
2
using System.Data;
3
using System.Data.Common;
4
using System.Globalization;
5
using System.Text;
6
using System.Text.Json;
7
using System.Text.Json.Serialization.Metadata;
8

9
using FluentCommand.Extensions;
10

11
using Microsoft.IO;
12

13
namespace FluentCommand;
14

15
/// <summary>
16
/// Extension methods for <see cref="IDataCommand"/>
17
/// </summary>
18
public static class JsonCommandExtensions
19
{
20
    private static readonly RecyclableMemoryStreamManager _memoryStreamManager = new();
1✔
21

22
    /// <summary>
23
    /// Adds a new parameter with the specified <paramref name="name" /> with the <paramref name="value" /> serialized as JSON using the specified <paramref name="options" />.
24
    /// </summary>
25
    /// <typeparam name="TParameter">The type of the parameter value.</typeparam>
26
    /// <param name="dataCommand">The <see cref="IDataCommand"/> for this extension method.</param>
27
    /// <param name="name">The name of the parameter.</param>
28
    /// <param name="value">The value to be serialized as JSON and added as a string parameter.</param>
29
    /// <param name="options">The <see cref="JsonSerializerOptions"/> to use when serializing.</param>
30
    /// <returns>
31
    /// A fluent <see langword="interface" /> to the data command.
32
    /// </returns>
33
    public static IDataCommand ParameterJson<TParameter>(
34
        this IDataCommand dataCommand,
35
        string name,
36
        TParameter value,
37
        JsonSerializerOptions? options = null)
38
    {
NEW
39
        var json = JsonSerializer.Serialize(value, options);
×
NEW
40
        return dataCommand.Parameter(name, json);
×
41
    }
42

43
    /// <summary>
44
    /// Adds a new parameter with the specified <paramref name="name" /> with the <paramref name="value" /> serialized as JSON using the specified <paramref name="jsonTypeInfo" />.
45
    /// </summary>
46
    /// <typeparam name="TParameter">The type of the parameter value.</typeparam>
47
    /// <param name="dataCommand">The <see cref="IDataCommand"/> for this extension method.</param>
48
    /// <param name="name">The name of the parameter.</param>
49
    /// <param name="value">The value to be serialized as JSON and added as a string parameter.</param>
50
    /// <param name="jsonTypeInfo">The <see cref="JsonTypeInfo{T}"/> to use when serializing.</param>
51
    /// <returns>
52
    /// A fluent <see langword="interface" /> to the data command.
53
    /// </returns>
54
    public static IDataCommand ParameterJson<TParameter>(
55
        this IDataCommand dataCommand,
56
        string name,
57
        TParameter value,
58
        JsonTypeInfo<TParameter> jsonTypeInfo)
59
    {
NEW
60
        var json = JsonSerializer.Serialize(value, jsonTypeInfo);
×
NEW
61
        return dataCommand.Parameter(name, json);
×
62
    }
63

64

65
    /// <summary>
66
    /// Executes the query and returns a JSON string from data set returned by the query.
67
    /// </summary>
68
    /// <param name="dataCommand">The data command.</param>
69
    /// <param name="options">The <see cref="JsonWriterOptions" /> options.</param>
70
    /// <returns>
71
    /// A JSON string representing the <see cref="IDataReader" /> result of the command.
72
    /// </returns>
73
    public static string QueryJson(
74
        this IDataCommand dataCommand,
75
        JsonWriterOptions options = default)
76
    {
77
        if (dataCommand is null)
1!
78
            throw new ArgumentNullException(nameof(dataCommand));
×
79

80
        using var stream = _memoryStreamManager.GetStream();
1✔
81

82
        QueryJson(dataCommand, stream, options);
1✔
83

84
        var bytes = stream.GetReadOnlySequence();
1✔
85

86
#if NET5_0_OR_GREATER
87
        return Encoding.UTF8.GetString(bytes);
1✔
88
#else
89
        return Encoding.UTF8.GetString(bytes.ToArray());
90
#endif
91
    }
1✔
92

93
    /// <summary>
94
    /// Executes the query and returns a JSON string from data set returned by the query.
95
    /// </summary>
96
    /// <param name="dataCommand">The data command.</param>
97
    /// <param name="stream">The destination for writing JSON text.</param>
98
    /// <param name="options">The <see cref="JsonWriterOptions" /> options.</param>
99
    /// <returns>
100
    /// A JSON string representing the <see cref="IDataReader" /> result of the command.
101
    /// </returns>
102
    public static void QueryJson(
103
        this IDataCommand dataCommand,
104
        Stream stream,
105
        JsonWriterOptions options = default)
106
    {
107
        if (dataCommand is null)
2!
108
            throw new ArgumentNullException(nameof(dataCommand));
×
109
        if (stream is null)
2!
110
            throw new ArgumentNullException(nameof(stream));
×
111

112
        var writer = new Utf8JsonWriter(stream, options);
2✔
113

114
        writer.WriteStartArray();
2✔
115

116
        dataCommand.Read(reader => WriteData(reader, writer), CommandBehavior.SequentialAccess | CommandBehavior.SingleResult);
4✔
117

118
        writer.WriteEndArray();
2✔
119

120
        writer.Flush();
2✔
121
    }
2✔
122

123

124
    /// <summary>
125
    /// Executes the query and returns a JSON string from data set returned by the query asynchronously.
126
    /// </summary>
127
    /// <param name="dataCommand">The data command.</param>
128
    /// <param name="options">The <see cref="JsonWriterOptions" /> options.</param>
129
    /// <param name="cancellationToken">The cancellation token.</param>
130
    /// <returns>
131
    /// A JSON string representing the <see cref="IDataReader" /> result of the command.
132
    /// </returns>
133
    public static async Task<string> QueryJsonAsync(
134
        this IDataCommand dataCommand,
135
        JsonWriterOptions options = default,
136
        CancellationToken cancellationToken = default)
137
    {
138
        if (dataCommand is null)
3!
139
            throw new ArgumentNullException(nameof(dataCommand));
×
140

141
        using var stream = _memoryStreamManager.GetStream();
3✔
142

143
        await QueryJsonAsync(dataCommand, stream, options, cancellationToken);
3✔
144

145
        var bytes = stream.GetReadOnlySequence();
3✔
146

147
#if NET5_0_OR_GREATER
148
        return Encoding.UTF8.GetString(bytes);
3✔
149
#else
150
        return Encoding.UTF8.GetString(bytes.ToArray());
151
#endif
152

153
    }
3✔
154

155
    /// <summary>
156
    /// Executes the query and returns a JSON string from data set returned by the query asynchronously.
157
    /// </summary>
158
    /// <param name="dataCommand">The data command.</param>
159
    /// <param name="stream">The destination for writing JSON text.</param>
160
    /// <param name="options">The <see cref="JsonWriterOptions" /> options.</param>
161
    /// <param name="cancellationToken">The cancellation token.</param>
162
    /// <returns>
163
    /// A JSON string representing the <see cref="IDataReader" /> result of the command.
164
    /// </returns>
165
    public static async Task QueryJsonAsync(
166
        this IDataCommand dataCommand,
167
        Stream stream,
168
        JsonWriterOptions options = default,
169
        CancellationToken cancellationToken = default)
170
    {
171
        if (dataCommand is null)
4!
172
            throw new ArgumentNullException(nameof(dataCommand));
×
173
        if (stream is null)
4!
174
            throw new ArgumentNullException(nameof(stream));
×
175

176
        var writer = new Utf8JsonWriter(stream, options);
4✔
177

178
        writer.WriteStartArray();
4✔
179

180
        await dataCommand.ReadAsync(async (reader, token) =>
4✔
181
        {
4✔
182
            if (reader is DbDataReader dataReader)
4!
183
                await WriteDataAsync(dataReader, writer, token);
4✔
184
            else
4✔
185
                WriteData(reader, writer);
×
186

4✔
187
        }, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, cancellationToken);
8✔
188

189
        writer.WriteEndArray();
4✔
190

191
        await writer.FlushAsync(cancellationToken);
4✔
192
    }
4✔
193

194

195
    private static void WriteData(
196
        IDataReader reader,
197
        Utf8JsonWriter writer)
198
    {
199
        while (reader.Read())
1,618✔
200
        {
201
            WriteObject(reader, writer);
1,616✔
202
        }
203
    }
2✔
204

205
    private static async Task WriteDataAsync(
206
        DbDataReader reader,
207
        Utf8JsonWriter writer,
208
        CancellationToken cancellationToken = default)
209
    {
210
        while (await reader.ReadAsync(cancellationToken))
1,629✔
211
        {
212
            WriteObject(reader, writer);
1,625✔
213
        }
214
    }
4✔
215

216
    private static void WriteObject(
217
        IDataReader reader,
218
        Utf8JsonWriter writer)
219
    {
220
        writer.WriteStartObject();
3,241✔
221

222
        for (int index = 0; index < reader.FieldCount; index++)
129,574✔
223
        {
224
            var name = reader.GetName(index);
61,546✔
225
            writer.WritePropertyName(name);
61,546✔
226

227
            WriteValue(reader, writer, index);
61,546✔
228
        }
229

230
        writer.WriteEndObject();
3,241✔
231
    }
3,241✔
232

233
    private static void WriteValue(
234
        IDataReader reader,
235
        Utf8JsonWriter writer,
236
        int index)
237
    {
238
        if (reader.IsDBNull(index))
61,546✔
239
        {
240
            writer.WriteNullValue();
22,704✔
241
            return;
22,704✔
242
        }
243

244
        var type = reader.GetFieldType(index);
38,842✔
245
        if (type == typeof(string))
38,842✔
246
        {
247
            var value = reader.GetString(index);
12,887✔
248
            writer.WriteStringValue(value);
12,887✔
249
            return;
12,887✔
250
        }
251

252
        if (type == typeof(bool))
25,955✔
253
        {
254
            var value = reader.GetBoolean(index);
9,707✔
255
            writer.WriteBooleanValue(value);
9,707✔
256
            return;
9,707✔
257
        }
258

259
        if (type == typeof(byte))
16,248!
260
        {
261
            var value = reader.GetByte(index);
×
262
            writer.WriteNumberValue(value);
×
263
            return;
×
264
        }
265

266
        if (type == typeof(short))
16,248✔
267
        {
268
            var value = reader.GetInt16(index);
5✔
269
            writer.WriteNumberValue(value);
5✔
270
            return;
5✔
271
        }
272

273
        if (type == typeof(int))
16,243✔
274
        {
275
            var value = reader.GetInt32(index);
3,244✔
276
            writer.WriteNumberValue(value);
3,244✔
277
            return;
3,244✔
278
        }
279

280
        if (type == typeof(long))
12,999✔
281
        {
282
            var value = reader.GetInt64(index);
8✔
283
            writer.WriteNumberValue(value);
8✔
284
            return;
8✔
285
        }
286

287
        if (type == typeof(float))
12,991✔
288
        {
289
            var value = reader.GetFloat(index);
5✔
290
            writer.WriteNumberValue(value);
5✔
291
            return;
5✔
292
        }
293

294
        if (type == typeof(double))
12,986✔
295
        {
296
            var value = reader.GetDouble(index);
5✔
297
            writer.WriteNumberValue(value);
5✔
298
            return;
5✔
299
        }
300

301
        if (type == typeof(decimal))
12,981✔
302
        {
303
            var value = reader.GetDecimal(index);
5✔
304
            writer.WriteNumberValue(value);
5✔
305
            return;
5✔
306
        }
307

308
#if NET6_0_OR_GREATER
309
        if (type == typeof(DateOnly))
12,976!
310
        {
311
            var value = reader.GetValue<DateOnly>(index);
×
312
            var formatted = value.ToString("yyyy'-'MM'-'dd", CultureInfo.InvariantCulture);
×
313

314
            writer.WriteStringValue(formatted);
×
315
            return;
×
316
        }
317

318
        if (type == typeof(TimeOnly))
12,976!
319
        {
320
            var value = reader.GetValue<TimeOnly>(index);
×
321
            string formatted = value.Second == 0 && value.Millisecond == 0
×
322
                ? value.ToString("HH':'mm", CultureInfo.InvariantCulture)
×
323
                : value.ToString(CultureInfo.InvariantCulture);
×
324

325
            writer.WriteStringValue(formatted);
×
326
            return;
×
327
        }
328
#endif
329

330
        if (type == typeof(TimeSpan))
12,976✔
331
        {
332
            var value = reader.GetValue<TimeSpan>(index);
10✔
333
            string formatted = value.Seconds == 0 && value.Milliseconds == 0
10✔
334
                ? value.ToString(@"hh\:mm", CultureInfo.InvariantCulture)
10✔
335
                : value.ToString();
10✔
336

337
            writer.WriteStringValue(formatted);
10✔
338
            return;
10✔
339
        }
340

341
        if (type == typeof(DateTime))
12,966✔
342
        {
343
            var value = reader.GetDateTime(index);
10✔
344
            var dataType = reader.GetDataTypeName(index).ToLowerInvariant();
10✔
345

346
            if (string.Equals(dataType, "date", StringComparison.OrdinalIgnoreCase))
10✔
347
            {
348
                var formattedDate = value.ToString("yyyy'-'MM'-'dd", CultureInfo.InvariantCulture);
5✔
349
                writer.WriteStringValue(formattedDate);
5✔
350
            }
351
            else
352
            {
353
                writer.WriteStringValue(value);
5✔
354
            }
355
            return;
5✔
356
        }
357

358
        if (type == typeof(DateTimeOffset))
12,956✔
359
        {
360
            var value = reader.GetValue(index);
6,481✔
361
            if (value is DateTimeOffset offset)
6,481!
362
            {
363
                writer.WriteStringValue(offset);
6,481✔
364
                return;
6,481✔
365
            }
366

367
            var date = reader.GetDateTime(index);
×
368
            date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
×
369

370
            offset = new DateTimeOffset(date, TimeSpan.Zero);
×
371

372
            writer.WriteStringValue(offset);
×
373
            return;
×
374
        }
375

376
        if (type == typeof(Guid))
6,475✔
377
        {
378
            var value = reader.GetGuid(index);
3,237✔
379
            writer.WriteStringValue(value);
3,237✔
380
            return;
3,237✔
381
        }
382

383
        // fallback
384
        var v = reader.GetValue(index);
3,238✔
385
        writer.WriteStringValue(v.ToString());
3,238✔
386
    }
3,238✔
387
}
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