• 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

75.34
/src/FluentCommand.Generators/DataReaderFactoryWriter.cs
1
using System.Text;
2

3
using FluentCommand.Generators.Models;
4

5
namespace FluentCommand.Generators;
6

7
public static class DataReaderFactoryWriter
8
{
9
    public static string Generate(EntityClass entityClass)
10
    {
11
        if (entityClass == null)
10!
12
            throw new ArgumentNullException(nameof(entityClass));
×
13

14
        var codeBuilder = new IndentedStringBuilder();
10✔
15
        codeBuilder
10✔
16
            .AppendLine("// <auto-generated />")
10✔
17
            .AppendLine("#nullable enable")
10✔
18
            .AppendLine()
10✔
19
            .AppendLine("using global::FluentCommand.Extensions;")
10✔
20
            .AppendLine();
10✔
21

22
        codeBuilder
10✔
23
            .Append("namespace ")
10✔
24
            .AppendLine(entityClass.EntityNamespace)
10✔
25
            .AppendLine("{")
10✔
26
            .IncrementIndent();
10✔
27

28
        codeBuilder
10✔
29
            .AppendLine("/// <summary>")
10✔
30
            .AppendLine("/// Extension methods for FluentCommand")
10✔
31
            .AppendLine("/// </summary>")
10✔
32
            .Append("public static partial class ")
10✔
33
            .Append(entityClass.EntityName)
10✔
34
            .AppendLine("DataReaderExtensions")
10✔
35
            .AppendLine("{")
10✔
36
            .IncrementIndent();
10✔
37

38
        var hasJsonColumns = HasJsonColumns(entityClass);
10✔
39

40
        WriteQueryEntity(codeBuilder, entityClass);
10✔
41

42
        if (hasJsonColumns)
10✔
43
            WriteQueryEntityJsonOptions(codeBuilder, entityClass);
2✔
44

45
        WriteQuerySingleEntity(codeBuilder, entityClass);
10✔
46

47
        if (hasJsonColumns)
10✔
48
            WriteQuerySingleEntityJsonOptions(codeBuilder, entityClass);
2✔
49

50
        WriteQueryEntityTask(codeBuilder, entityClass);
10✔
51

52
        if (hasJsonColumns)
10✔
53
            WriteQueryEntityTaskJsonOptions(codeBuilder, entityClass);
2✔
54

55
        WriteQuerySingleEntityTask(codeBuilder, entityClass);
10✔
56

57
        if (hasJsonColumns)
10✔
58
            WriteQuerySingleEntityTaskJsonOptions(codeBuilder, entityClass);
2✔
59

60
        WriteEntityFactory(codeBuilder, entityClass);
10✔
61

62
        codeBuilder
10✔
63
            .DecrementIndent()
10✔
64
            .AppendLine("}") // class
10✔
65
            .DecrementIndent()
10✔
66
            .AppendLine("}"); // namespace
10✔
67

68
        return codeBuilder.ToString();
10✔
69
    }
70

71
    private static void WriteQuerySingleEntityTask(IndentedStringBuilder codeBuilder, EntityClass entity)
72
    {
73
        // public static Task<Entity> QuerySingleEntityAsync(this IDataQueryAsync dataQuery) => QuerySingleAsync(dataQuery, EntityFactory);
74
        codeBuilder
10✔
75
            .AppendLine("/// <summary>")
10✔
76
            .Append("/// Executes the query and returns the first row in the result as a <see cref=\"T:")
10✔
77
            .Append(entity.FullyQualified)
10✔
78
            .AppendLine("\"/> object.")
10✔
79
            .AppendLine("/// </summary>")
10✔
80
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQueryAsync\"/> for this extension method.</param>")
10✔
81
            .AppendLine("/// <param name=\"cancellationToken\">The cancellation instruction.</param>")
10✔
82
            .AppendLine("/// <returns>")
10✔
83
            .Append("/// A instance of <see cref=\"T:")
10✔
84
            .Append(entity.FullyQualified)
10✔
85
            .AppendLine("\"/>  if row exists; otherwise null.")
10✔
86
            .AppendLine("/// </returns>")
10✔
87
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
10✔
88
            .Append(ThisAssembly.Product)
10✔
89
            .Append("\", \"")
10✔
90
            .Append(ThisAssembly.InformationalVersion)
10✔
91
            .AppendLine("\")]")
10✔
92
            .Append("public static global::System.Threading.Tasks.Task<")
10✔
93
            .Append(entity.FullyQualified)
10✔
94
            .Append("?> QuerySingle")
10✔
95
            .AppendLine("Async<TEntity>(")
10✔
96
            .IncrementIndent()
10✔
97
            .AppendLine("this global::FluentCommand.IDataQueryAsync dataQuery,")
10✔
98
            .AppendLine("global::System.Threading.CancellationToken cancellationToken = default)")
10✔
99
            .Append("where TEntity : ")
10✔
100
            .AppendLine(entity.FullyQualified)
10✔
101
            .DecrementIndent()
10✔
102
            .AppendLine("{")
10✔
103
            .IncrementIndent()
10✔
104
            .Append("return dataQuery.QuerySingleAsync<")
10✔
105
            .Append(entity.FullyQualified)
10✔
106
            .AppendLine(">(")
10✔
107
            .IncrementIndent()
10✔
108
            .Append("factory: ")
10✔
109
            .Append(entity.EntityName)
10✔
110
            .Append("DataReaderExtensions.")
10✔
111
            .Append(entity.EntityName)
10✔
112
            .AppendLine("Factory,")
10✔
113
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
10✔
114
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult |")
10✔
115
            .AppendLine("                 global::System.Data.CommandBehavior.SingleRow,")
10✔
116
            .AppendLine("cancellationToken: cancellationToken);")
10✔
117
            .DecrementIndent()
10✔
118
            .DecrementIndent()
10✔
119
            .AppendLine("}")
10✔
120
            .AppendLine();
10✔
121
    }
10✔
122

123
    private static void WriteQuerySingleEntityTaskJsonOptions(IndentedStringBuilder codeBuilder, EntityClass entity)
124
    {
125
        codeBuilder
2✔
126
            .AppendLine("/// <summary>")
2✔
127
            .Append("/// Executes the query and returns the first row in the result as a <see cref=\"T:")
2✔
128
            .Append(entity.FullyQualified)
2✔
129
            .AppendLine("\"/> object.")
2✔
130
            .AppendLine("/// </summary>")
2✔
131
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQueryAsync\"/> for this extension method.</param>")
2✔
132
            .AppendLine("/// <param name=\"jsonSerializerOptions\">The JSON serializer options to use for JSON columns.</param>")
2✔
133
            .AppendLine("/// <param name=\"cancellationToken\">The cancellation instruction.</param>")
2✔
134
            .AppendLine("/// <returns>")
2✔
135
            .Append("/// A instance of <see cref=\"T:")
2✔
136
            .Append(entity.FullyQualified)
2✔
137
            .AppendLine("\"/>  if row exists; otherwise null.")
2✔
138
            .AppendLine("/// </returns>")
2✔
139
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
2✔
140
            .Append(ThisAssembly.Product)
2✔
141
            .Append("\", \"")
2✔
142
            .Append(ThisAssembly.InformationalVersion)
2✔
143
            .AppendLine("\")]")
2✔
144
            .Append("public static global::System.Threading.Tasks.Task<")
2✔
145
            .Append(entity.FullyQualified)
2✔
146
            .Append("?> QuerySingle")
2✔
147
            .AppendLine("Async<TEntity>(")
2✔
148
            .IncrementIndent()
2✔
149
            .AppendLine("this global::FluentCommand.IDataQueryAsync dataQuery,")
2✔
150
            .AppendLine("global::System.Text.Json.JsonSerializerOptions? jsonSerializerOptions,")
2✔
151
            .AppendLine("global::System.Threading.CancellationToken cancellationToken = default)")
2✔
152
            .Append("where TEntity : ")
2✔
153
            .AppendLine(entity.FullyQualified)
2✔
154
            .DecrementIndent()
2✔
155
            .AppendLine("{")
2✔
156
            .IncrementIndent()
2✔
157
            .Append("return dataQuery.QuerySingleAsync<")
2✔
158
            .Append(entity.FullyQualified)
2✔
159
            .AppendLine(">(")
2✔
160
            .IncrementIndent()
2✔
161
            .Append("factory: r => ")
2✔
162
            .Append(entity.EntityName)
2✔
163
            .Append("DataReaderExtensions.")
2✔
164
            .Append(entity.EntityName)
2✔
165
            .AppendLine("Factory(r, jsonSerializerOptions),")
2✔
166
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
2✔
167
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult |")
2✔
168
            .AppendLine("                 global::System.Data.CommandBehavior.SingleRow,")
2✔
169
            .AppendLine("cancellationToken: cancellationToken);")
2✔
170
            .DecrementIndent()
2✔
171
            .DecrementIndent()
2✔
172
            .AppendLine("}")
2✔
173
            .AppendLine();
2✔
174
    }
2✔
175

176
    private static void WriteQueryEntityTask(IndentedStringBuilder codeBuilder, EntityClass entity)
177
    {
178
        // public static Task<IEnumerable<Entity>> QueryEntityAsync(this IDataQueryAsync dataQuery) => QueryAsync(dataQuery, EntityFactory);
179
        codeBuilder
10✔
180
            .AppendLine("/// <summary>")
10✔
181
            .Append("/// Executes the command against the connection and converts the results to <see cref=\"T:")
10✔
182
            .Append(entity.FullyQualified)
10✔
183
            .AppendLine("\"/> objects.")
10✔
184
            .AppendLine("/// </summary>")
10✔
185
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQueryAsync\"/> for this extension method.</param>")
10✔
186
            .AppendLine("/// <param name=\"cancellationToken\">The cancellation instruction.</param>")
10✔
187
            .AppendLine("/// <returns>")
10✔
188
            .Append("/// An <see cref=\"T:System.Collections.Generic.IEnumerable`1\" /> of <see cref=\"T:")
10✔
189
            .Append(entity.FullyQualified)
10✔
190
            .AppendLine("\"/> objects.")
10✔
191
            .AppendLine("/// </returns>")
10✔
192
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
10✔
193
            .Append(ThisAssembly.Product)
10✔
194
            .Append("\", \"")
10✔
195
            .Append(ThisAssembly.InformationalVersion)
10✔
196
            .AppendLine("\")]")
10✔
197
            .Append("public static global::System.Threading.Tasks.Task<global::System.Collections.Generic.IEnumerable<")
10✔
198
            .Append(entity.FullyQualified)
10✔
199
            .AppendLine(">> QueryAsync<TEntity>(")
10✔
200
            .IncrementIndent()
10✔
201
            .AppendLine("this global::FluentCommand.IDataQueryAsync dataQuery,")
10✔
202
            .AppendLine("global::System.Threading.CancellationToken cancellationToken = default)")
10✔
203
            .Append("where TEntity : ")
10✔
204
            .AppendLine(entity.FullyQualified)
10✔
205
            .DecrementIndent()
10✔
206
            .AppendLine("{")
10✔
207
            .IncrementIndent()
10✔
208
            .Append("return dataQuery.QueryAsync<")
10✔
209
            .Append(entity.FullyQualified)
10✔
210
            .AppendLine(">(")
10✔
211
            .IncrementIndent()
10✔
212
            .Append("factory: ")
10✔
213
            .Append(entity.EntityName)
10✔
214
            .Append("DataReaderExtensions.")
10✔
215
            .Append(entity.EntityName)
10✔
216
            .AppendLine("Factory,")
10✔
217
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
10✔
218
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult,")
10✔
219
            .AppendLine("cancellationToken: cancellationToken);")
10✔
220
            .DecrementIndent()
10✔
221
            .DecrementIndent()
10✔
222
            .AppendLine("}")
10✔
223
            .AppendLine();
10✔
224
    }
10✔
225

226
    private static void WriteQueryEntityTaskJsonOptions(IndentedStringBuilder codeBuilder, EntityClass entity)
227
    {
228
        codeBuilder
2✔
229
            .AppendLine("/// <summary>")
2✔
230
            .Append("/// Executes the command against the connection and converts the results to <see cref=\"T:")
2✔
231
            .Append(entity.FullyQualified)
2✔
232
            .AppendLine("\"/> objects.")
2✔
233
            .AppendLine("/// </summary>")
2✔
234
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQueryAsync\"/> for this extension method.</param>")
2✔
235
            .AppendLine("/// <param name=\"jsonSerializerOptions\">The JSON serializer options to use for JSON columns.</param>")
2✔
236
            .AppendLine("/// <param name=\"cancellationToken\">The cancellation instruction.</param>")
2✔
237
            .AppendLine("/// <returns>")
2✔
238
            .Append("/// An <see cref=\"T:System.Collections.Generic.IEnumerable`1\" /> of <see cref=\"T:")
2✔
239
            .Append(entity.FullyQualified)
2✔
240
            .AppendLine("\"/> objects.")
2✔
241
            .AppendLine("/// </returns>")
2✔
242
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
2✔
243
            .Append(ThisAssembly.Product)
2✔
244
            .Append("\", \"")
2✔
245
            .Append(ThisAssembly.InformationalVersion)
2✔
246
            .AppendLine("\")]")
2✔
247
            .Append("public static global::System.Threading.Tasks.Task<global::System.Collections.Generic.IEnumerable<")
2✔
248
            .Append(entity.FullyQualified)
2✔
249
            .AppendLine(">> QueryAsync<TEntity>(")
2✔
250
            .IncrementIndent()
2✔
251
            .AppendLine("this global::FluentCommand.IDataQueryAsync dataQuery,")
2✔
252
            .AppendLine("global::System.Text.Json.JsonSerializerOptions? jsonSerializerOptions,")
2✔
253
            .AppendLine("global::System.Threading.CancellationToken cancellationToken = default)")
2✔
254
            .Append("where TEntity : ")
2✔
255
            .AppendLine(entity.FullyQualified)
2✔
256
            .DecrementIndent()
2✔
257
            .AppendLine("{")
2✔
258
            .IncrementIndent()
2✔
259
            .Append("return dataQuery.QueryAsync<")
2✔
260
            .Append(entity.FullyQualified)
2✔
261
            .AppendLine(">(")
2✔
262
            .IncrementIndent()
2✔
263
            .Append("factory: r => ")
2✔
264
            .Append(entity.EntityName)
2✔
265
            .Append("DataReaderExtensions.")
2✔
266
            .Append(entity.EntityName)
2✔
267
            .AppendLine("Factory(r, jsonSerializerOptions),")
2✔
268
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
2✔
269
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult,")
2✔
270
            .AppendLine("cancellationToken: cancellationToken);")
2✔
271
            .DecrementIndent()
2✔
272
            .DecrementIndent()
2✔
273
            .AppendLine("}")
2✔
274
            .AppendLine();
2✔
275
    }
2✔
276

277
    private static void WriteQuerySingleEntity(IndentedStringBuilder codeBuilder, EntityClass entity)
278
    {
279
        // public static Entity QuerySingleEntity(this IDataQuery dataQuery) => QuerySingle(dataQuery, EntityFactory);
280
        codeBuilder
10✔
281
            .AppendLine("/// <summary>")
10✔
282
            .Append("/// Executes the query and returns the first row in the result as a <see cref=\"T:")
10✔
283
            .Append(entity.FullyQualified)
10✔
284
            .AppendLine("\"/> object.")
10✔
285
            .AppendLine("/// </summary>")
10✔
286
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQuery\"/> for this extension method.</param>")
10✔
287
            .AppendLine("/// <returns>")
10✔
288
            .Append("/// A instance of <see cref=\"T:")
10✔
289
            .Append(entity.FullyQualified)
10✔
290
            .AppendLine("\"/>  if row exists; otherwise null.")
10✔
291
            .AppendLine("/// </returns>")
10✔
292
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
10✔
293
            .Append(ThisAssembly.Product)
10✔
294
            .Append("\", \"")
10✔
295
            .Append(ThisAssembly.InformationalVersion)
10✔
296
            .AppendLine("\")]")
10✔
297
            .Append("public static ")
10✔
298
            .Append(entity.FullyQualified)
10✔
299
            .AppendLine("? QuerySingle<TEntity>(")
10✔
300
            .IncrementIndent()
10✔
301
            .AppendLine("this global::FluentCommand.IDataQuery dataQuery)")
10✔
302
            .Append("where TEntity : ")
10✔
303
            .AppendLine(entity.FullyQualified)
10✔
304
            .DecrementIndent()
10✔
305
            .AppendLine("{")
10✔
306
            .IncrementIndent()
10✔
307
            .Append("return dataQuery.QuerySingle<")
10✔
308
            .Append(entity.FullyQualified)
10✔
309
            .AppendLine(">(")
10✔
310
            .IncrementIndent()
10✔
311
            .Append("factory: ")
10✔
312
            .Append(entity.EntityName)
10✔
313
            .Append("DataReaderExtensions.")
10✔
314
            .Append(entity.EntityName)
10✔
315
            .AppendLine("Factory,")
10✔
316
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
10✔
317
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult |")
10✔
318
            .AppendLine("                 global::System.Data.CommandBehavior.SingleRow);")
10✔
319
            .DecrementIndent()
10✔
320
            .DecrementIndent()
10✔
321
            .AppendLine("}")
10✔
322
            .AppendLine();
10✔
323
    }
10✔
324

325
    private static void WriteQuerySingleEntityJsonOptions(IndentedStringBuilder codeBuilder, EntityClass entity)
326
    {
327
        codeBuilder
2✔
328
            .AppendLine("/// <summary>")
2✔
329
            .Append("/// Executes the query and returns the first row in the result as a <see cref=\"T:")
2✔
330
            .Append(entity.FullyQualified)
2✔
331
            .AppendLine("\"/> object.")
2✔
332
            .AppendLine("/// </summary>")
2✔
333
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQuery\"/> for this extension method.</param>")
2✔
334
            .AppendLine("/// <param name=\"jsonSerializerOptions\">The JSON serializer options to use for JSON columns.</param>")
2✔
335
            .AppendLine("/// <returns>")
2✔
336
            .Append("/// A instance of <see cref=\"T:")
2✔
337
            .Append(entity.FullyQualified)
2✔
338
            .AppendLine("\"/>  if row exists; otherwise null.")
2✔
339
            .AppendLine("/// </returns>")
2✔
340
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
2✔
341
            .Append(ThisAssembly.Product)
2✔
342
            .Append("\", \"")
2✔
343
            .Append(ThisAssembly.InformationalVersion)
2✔
344
            .AppendLine("\")]")
2✔
345
            .Append("public static ")
2✔
346
            .Append(entity.FullyQualified)
2✔
347
            .AppendLine("? QuerySingle<TEntity>(")
2✔
348
            .IncrementIndent()
2✔
349
            .AppendLine("this global::FluentCommand.IDataQuery dataQuery,")
2✔
350
            .AppendLine("global::System.Text.Json.JsonSerializerOptions? jsonSerializerOptions)")
2✔
351
            .Append("where TEntity : ")
2✔
352
            .AppendLine(entity.FullyQualified)
2✔
353
            .DecrementIndent()
2✔
354
            .AppendLine("{")
2✔
355
            .IncrementIndent()
2✔
356
            .Append("return dataQuery.QuerySingle<")
2✔
357
            .Append(entity.FullyQualified)
2✔
358
            .AppendLine(">(")
2✔
359
            .IncrementIndent()
2✔
360
            .Append("factory: r => ")
2✔
361
            .Append(entity.EntityName)
2✔
362
            .Append("DataReaderExtensions.")
2✔
363
            .Append(entity.EntityName)
2✔
364
            .AppendLine("Factory(r, jsonSerializerOptions),")
2✔
365
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
2✔
366
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult |")
2✔
367
            .AppendLine("                 global::System.Data.CommandBehavior.SingleRow);")
2✔
368
            .DecrementIndent()
2✔
369
            .DecrementIndent()
2✔
370
            .AppendLine("}")
2✔
371
            .AppendLine();
2✔
372
    }
2✔
373

374
    private static void WriteQueryEntity(IndentedStringBuilder codeBuilder, EntityClass entity)
375
    {
376
        // public static IEnumerable<Entity> QueryEntity(this IDataQuery dataQuery) => Query(dataQuery, EntityFactory);
377
        codeBuilder
10✔
378
            .AppendLine("/// <summary>")
10✔
379
            .Append("/// Executes the command against the connection and converts the results to <see cref=\"T:")
10✔
380
            .Append(entity.FullyQualified)
10✔
381
            .AppendLine("\"/> objects.")
10✔
382
            .AppendLine("/// </summary>")
10✔
383
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQuery\"/> for this extension method.</param>")
10✔
384
            .AppendLine("/// <returns>")
10✔
385
            .Append("/// An <see cref=\"T:System.Collections.Generic.IEnumerable`1\" /> of <see cref=\"T:")
10✔
386
            .Append(entity.FullyQualified)
10✔
387
            .AppendLine("\"/> objects.")
10✔
388
            .AppendLine("/// </returns>")
10✔
389
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
10✔
390
            .Append(ThisAssembly.Product)
10✔
391
            .Append("\", \"")
10✔
392
            .Append(ThisAssembly.InformationalVersion)
10✔
393
            .AppendLine("\")]")
10✔
394
            .Append("public static global::System.Collections.Generic.IEnumerable<")
10✔
395
            .Append(entity.FullyQualified)
10✔
396
            .AppendLine("> Query<TEntity>(")
10✔
397
            .IncrementIndent()
10✔
398
            .AppendLine("this global::FluentCommand.IDataQuery dataQuery)")
10✔
399
            .Append("where TEntity : ")
10✔
400
            .AppendLine(entity.FullyQualified)
10✔
401
            .DecrementIndent()
10✔
402
            .AppendLine("{")
10✔
403
            .IncrementIndent()
10✔
404
            .Append("return dataQuery.Query<")
10✔
405
            .Append(entity.FullyQualified)
10✔
406
            .AppendLine(">(")
10✔
407
            .IncrementIndent()
10✔
408
            .Append("factory: ")
10✔
409
            .Append(entity.EntityName)
10✔
410
            .Append("DataReaderExtensions.")
10✔
411
            .Append(entity.EntityName)
10✔
412
            .AppendLine("Factory,")
10✔
413
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
10✔
414
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult);")
10✔
415
            .DecrementIndent()
10✔
416
            .DecrementIndent()
10✔
417
            .AppendLine("}")
10✔
418
            .AppendLine();
10✔
419
    }
10✔
420

421
    private static void WriteQueryEntityJsonOptions(IndentedStringBuilder codeBuilder, EntityClass entity)
422
    {
423
        codeBuilder
2✔
424
            .AppendLine("/// <summary>")
2✔
425
            .Append("/// Executes the command against the connection and converts the results to <see cref=\"T:")
2✔
426
            .Append(entity.FullyQualified)
2✔
427
            .AppendLine("\"/> objects.")
2✔
428
            .AppendLine("/// </summary>")
2✔
429
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQuery\"/> for this extension method.</param>")
2✔
430
            .AppendLine("/// <param name=\"jsonSerializerOptions\">The JSON serializer options to use for JSON columns.</param>")
2✔
431
            .AppendLine("/// <returns>")
2✔
432
            .Append("/// An <see cref=\"T:System.Collections.Generic.IEnumerable`1\" /> of <see cref=\"T:")
2✔
433
            .Append(entity.FullyQualified)
2✔
434
            .AppendLine("\"/> objects.")
2✔
435
            .AppendLine("/// </returns>")
2✔
436
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
2✔
437
            .Append(ThisAssembly.Product)
2✔
438
            .Append("\", \"")
2✔
439
            .Append(ThisAssembly.InformationalVersion)
2✔
440
            .AppendLine("\")]")
2✔
441
            .Append("public static global::System.Collections.Generic.IEnumerable<")
2✔
442
            .Append(entity.FullyQualified)
2✔
443
            .AppendLine("> Query<TEntity>(")
2✔
444
            .IncrementIndent()
2✔
445
            .AppendLine("this global::FluentCommand.IDataQuery dataQuery,")
2✔
446
            .AppendLine("global::System.Text.Json.JsonSerializerOptions? jsonSerializerOptions)")
2✔
447
            .Append("where TEntity : ")
2✔
448
            .AppendLine(entity.FullyQualified)
2✔
449
            .DecrementIndent()
2✔
450
            .AppendLine("{")
2✔
451
            .IncrementIndent()
2✔
452
            .Append("return dataQuery.Query<")
2✔
453
            .Append(entity.FullyQualified)
2✔
454
            .AppendLine(">(")
2✔
455
            .IncrementIndent()
2✔
456
            .Append("factory: r => ")
2✔
457
            .Append(entity.EntityName)
2✔
458
            .Append("DataReaderExtensions.")
2✔
459
            .Append(entity.EntityName)
2✔
460
            .AppendLine("Factory(r, jsonSerializerOptions),")
2✔
461
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
2✔
462
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult);")
2✔
463
            .DecrementIndent()
2✔
464
            .DecrementIndent()
2✔
465
            .AppendLine("}")
2✔
466
            .AppendLine();
2✔
467
    }
2✔
468

469
    private static void WriteEntityFactory(IndentedStringBuilder codeBuilder, EntityClass entity)
470
    {
471
        var hasJsonColumns = HasJsonColumns(entity);
10✔
472

473
        codeBuilder
10✔
474
            .AppendLine("/// <summary>")
10✔
475
            .Append("/// A factory for creating <see cref=\"T:")
10✔
476
            .Append(entity.FullyQualified)
10✔
477
            .AppendLine("\"/> objects from the current row in the specified <paramref name=\"dataRecord\"/>.")
10✔
478
            .AppendLine("/// </summary>")
10✔
479
            .AppendLine("/// <param name=\"dataRecord\">The open <see cref=\"T:System.Data.IDataReader\"/> to get the object from.</param>")
10✔
480
            .AppendLine("/// <returns>")
10✔
481
            .Append("/// A instance of <see cref=\"")
10✔
482
            .Append(entity.FullyQualified)
10✔
483
            .AppendLine("\"/>  having property names set that match the field names in the <paramref name=\"dataRecord\"/>.")
10✔
484
            .AppendLine("/// </returns>")
10✔
485
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
10✔
486
            .Append(ThisAssembly.Product)
10✔
487
            .Append("\", \"")
10✔
488
            .Append(ThisAssembly.InformationalVersion)
10✔
489
            .AppendLine("\")]")
10✔
490
            .Append("public static ")
10✔
491
            .Append(entity.FullyQualified)
10✔
492
            .Append(" ")
10✔
493
            .Append(entity.EntityName)
10✔
494
            .AppendLine("Factory(this global::System.Data.IDataReader dataRecord)")
10✔
495
            .AppendLine("{")
10✔
496
            .IncrementIndent()
10✔
497
            .AppendLine("if (dataRecord == null)")
10✔
498
            .IncrementIndent()
10✔
499
            .AppendLine("throw new global::System.ArgumentNullException(nameof(dataRecord));")
10✔
500
            .DecrementIndent()
10✔
501
            .AppendLine();
10✔
502

503
        if (hasJsonColumns)
10✔
504
        {
505
            codeBuilder
2✔
506
                .AppendLine("var __jsonSerializerOptions = dataRecord is global::FluentCommand.IDataReaderContext __context")
2✔
507
                .IncrementIndent()
2✔
508
                .AppendLine("? __context.JsonSerializerOptions")
2✔
509
                .AppendLine(": null;")
2✔
510
                .DecrementIndent()
2✔
511
                .AppendLine()
2✔
512
                .Append("return ")
2✔
513
                .Append(entity.EntityName)
2✔
514
                .AppendLine("Factory(dataRecord, __jsonSerializerOptions);")
2✔
515
                .DecrementIndent()
2✔
516
                .AppendLine("}")
2✔
517
                .AppendLine();
2✔
518

519
            WriteEntityFactoryWithJsonOptions(codeBuilder, entity);
2✔
520
            return;
2✔
521
        }
522

523
        WriteEntityFactoryBody(codeBuilder, entity, false);
8✔
524
    }
8✔
525

526
    private static void WriteEntityFactoryWithJsonOptions(IndentedStringBuilder codeBuilder, EntityClass entity)
527
    {
528
        codeBuilder
2✔
529
            .AppendLine("/// <summary>")
2✔
530
            .Append("/// A factory for creating <see cref=\"T:")
2✔
531
            .Append(entity.FullyQualified)
2✔
532
            .AppendLine("\"/> objects from the current row in the specified <paramref name=\"dataRecord\"/>.")
2✔
533
            .AppendLine("/// </summary>")
2✔
534
            .AppendLine("/// <param name=\"dataRecord\">The open <see cref=\"T:System.Data.IDataReader\"/> to get the object from.</param>")
2✔
535
            .AppendLine("/// <param name=\"jsonSerializerOptions\">The JSON serializer options to use for JSON columns.</param>")
2✔
536
            .AppendLine("/// <returns>")
2✔
537
            .Append("/// A instance of <see cref=\"")
2✔
538
            .Append(entity.FullyQualified)
2✔
539
            .AppendLine("\"/>  having property names set that match the field names in the <paramref name=\"dataRecord\"/>.")
2✔
540
            .AppendLine("/// </returns>")
2✔
541
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
2✔
542
            .Append(ThisAssembly.Product)
2✔
543
            .Append("\", \"")
2✔
544
            .Append(ThisAssembly.InformationalVersion)
2✔
545
            .AppendLine("\")]")
2✔
546
            .Append("public static ")
2✔
547
            .Append(entity.FullyQualified)
2✔
548
            .Append(" ")
2✔
549
            .Append(entity.EntityName)
2✔
550
            .AppendLine("Factory(")
2✔
551
            .IncrementIndent()
2✔
552
            .AppendLine("this global::System.Data.IDataReader dataRecord,")
2✔
553
            .AppendLine("global::System.Text.Json.JsonSerializerOptions? jsonSerializerOptions)")
2✔
554
            .DecrementIndent()
2✔
555
            .AppendLine("{")
2✔
556
            .IncrementIndent()
2✔
557
            .AppendLine("if (dataRecord == null)")
2✔
558
            .IncrementIndent()
2✔
559
            .AppendLine("throw new global::System.ArgumentNullException(nameof(dataRecord));")
2✔
560
            .DecrementIndent()
2✔
561
            .AppendLine();
2✔
562

563
        WriteEntityFactoryBody(codeBuilder, entity, true);
2✔
564
    }
2✔
565

566
    private static void WriteEntityFactoryBody(IndentedStringBuilder codeBuilder, EntityClass entity, bool hasJsonOptionsParameter)
567
    {
568

569
        foreach (var entityProperty in entity.Properties)
78✔
570
        {
571
            if (entityProperty.IsNotMapped)
29✔
572
                continue;
573

574
            var aliasType = GetAliasMap(entityProperty.PropertyType);
27✔
575
            var fieldName = CamelCase(entityProperty.PropertyName);
27✔
576

577
            // local variable
578
            codeBuilder
27✔
579
                .Append(aliasType)
27✔
580
                .Append(" v_")
27✔
581
                .Append(fieldName)
27✔
582
                .Append(" = default")
27✔
583
                .AppendIf("!", _ => !entityProperty.IsNullable)
27✔
584
                .AppendLine(";");
27✔
585
        }
586

587
        codeBuilder
10✔
588
            .AppendLine()
10✔
589
            .AppendLine("for (var __index = 0; __index < dataRecord.FieldCount; __index++)")
10✔
590
            .AppendLine("{")
10✔
591
            .IncrementIndent()
10✔
592
            .AppendLine("if (dataRecord.IsDBNull(__index))")
10✔
593
            .IncrementIndent()
10✔
594
            .AppendLine(" continue;")
10✔
595
            .DecrementIndent()
10✔
596
            .AppendLine();
10✔
597

598
        codeBuilder
10✔
599
            .AppendLine("var __name = dataRecord.GetName(__index);")
10✔
600
            .AppendLine("switch (__name)")
10✔
601
            .AppendLine("{")
10✔
602
            .IncrementIndent();
10✔
603

604
        foreach (var entityProperty in entity.Properties)
78✔
605
        {
606
            if (entityProperty.IsNotMapped)
29✔
607
                continue;
608

609
            var fieldName = CamelCase(entityProperty.PropertyName);
27✔
610

611
            codeBuilder
27✔
612
                .Append("case \"")
27✔
613
                .Append(entityProperty.ColumnName)
27✔
614
                .AppendLine("\":");
27✔
615

616
            if (entityProperty.IsJsonColumn)
27✔
617
            {
618
                var jsonReaderName = entityProperty.IsNullable
3✔
619
                    ? "GetFromJson"
3✔
620
                    : "GetRequiredFromJson";
3✔
621

622
                codeBuilder
3✔
623
                    .IncrementIndent()
3✔
624
                    .Append("v_")
3✔
625
                    .Append(fieldName)
3✔
626
                    .Append(" = dataRecord.")
3✔
627
                    .Append(jsonReaderName)
3✔
628
                    .Append("<")
3✔
629
                    .Append(entityProperty.PropertyType)
3✔
630
                    .Append(">(__index")
3✔
631
                    .AppendIf(", jsonSerializerOptions", _ => hasJsonOptionsParameter)
3✔
632
                    .AppendLine(");")
3✔
633
                    .AppendLine("break;")
3✔
634
                    .DecrementIndent();
3✔
635
            }
636
            else if (entityProperty.IsEnum)
24✔
637
            {
638
                WriteEnumColumnReader(codeBuilder, entityProperty, fieldName);
2✔
639
            }
640
            else if (string.IsNullOrEmpty(entityProperty.ConverterName))
22✔
641
            {
642
                var readerName = GetReaderName(entityProperty);
21✔
643

644
                codeBuilder
21✔
645
                    .IncrementIndent()
21✔
646
                    .Append("v_")
21✔
647
                    .Append(fieldName)
21✔
648
                    .Append(" = dataRecord.")
21✔
649
                    .Append(readerName)
21✔
650
                    .AppendLine("(__index);")
21✔
651
                    .AppendLine("break;")
21✔
652
                    .DecrementIndent();
21✔
653
            }
654
            else
655
            {
656
                // create converter via singleton helper
657
                codeBuilder
1✔
658
                    .IncrementIndent()
1✔
659
                    .Append("var c_")
1✔
660
                    .Append(fieldName)
1✔
661
                    .Append(" = global::FluentCommand.Internal.Singleton<")
1✔
662
                    .Append(entityProperty.ConverterName!)
1✔
663
                    .AppendLine(">.Current")
1✔
664
                    .IncrementIndent()
1✔
665
                    .Append("as global::FluentCommand.IDataFieldConverter<")
1✔
666
                    .Append(entityProperty.PropertyType)
1✔
667
                    .AppendLine(">;")
1✔
668
                    .DecrementIndent()
1✔
669
                    .AppendLine();
1✔
670

671
                // read via converter instance
672
                codeBuilder
1✔
673
                    .Append("v_")
1✔
674
                    .Append(fieldName)
1✔
675
                    .Append(" = c_")
1✔
676
                    .Append(fieldName)
1✔
677
                    .AppendLine(".ReadValue(dataRecord, __index);")
1✔
678
                    .AppendLine("break;")
1✔
679
                    .DecrementIndent();
1✔
680
            }
681
        }
682

683
        codeBuilder
10✔
684
            .DecrementIndent()
10✔
685
            .AppendLine("}") // switch
10✔
686
            .DecrementIndent()
10✔
687
            .AppendLine("}") // for
10✔
688
            .AppendLine();
10✔
689

690
        if (entity.InitializationMode == InitializationMode.Constructor)
10✔
691
            WriteReturnConstructor(codeBuilder, entity);
2✔
692
        else
693
            WriteReturnObjectInitializer(codeBuilder, entity);
8✔
694

695
        codeBuilder
10✔
696
            .DecrementIndent()
10✔
697
            .AppendLine("}") // method
10✔
698
            .AppendLine();
10✔
699
    }
10✔
700

701
    private static void WriteEnumColumnReader(IndentedStringBuilder codeBuilder, EntityProperty entityProperty, string fieldName)
702
    {
703
        codeBuilder
2✔
704
            .IncrementIndent()
2✔
705
            .Append("v_")
2✔
706
            .Append(fieldName)
2✔
707
            .Append(" = ")
2✔
708
            .Append(GetEnumReadExpression(entityProperty))
2✔
709
            .AppendLine(";")
2✔
710
            .AppendLine("break;")
2✔
711
            .DecrementIndent();
2✔
712
    }
2✔
713

714
    private static string GetEnumReadExpression(EntityProperty entityProperty)
715
    {
716
        var underlyingType = GetAliasMap(entityProperty.EnumUnderlyingType ?? "int");
2!
717

718
        if (entityProperty.IsNullableEnum)
2✔
719
            return $"({entityProperty.PropertyType})dataRecord.GetValue<{underlyingType}?>(__index)";
1✔
720

721
        return $"({entityProperty.PropertyType})dataRecord.{GetReaderName(underlyingType)}(__index)";
1✔
722
    }
723

724
    private static void WriteReturnConstructor(IndentedStringBuilder codeBuilder, EntityClass entity)
725
    {
726
        codeBuilder
2✔
727
            .Append("return new ")
2✔
728
            .Append(entity.FullyQualified)
2✔
729
            .AppendLine("(")
2✔
730
            .IncrementIndent();
2✔
731

732
        var mappedProperties = entity.Properties.Where(p => !p.IsNotMapped).ToList();
2✔
733
        var index = 0;
2✔
734
        var count = mappedProperties.Count;
2✔
735

736
        foreach (var entityProperty in mappedProperties)
16✔
737
        {
738
            var fieldName = CamelCase(entityProperty.PropertyName);
6✔
739
            codeBuilder
6✔
740
                .Append(entityProperty.ParameterName!)
6✔
741
                .Append(": ")
6✔
742
                .Append(" v_")
6✔
743
                .Append(fieldName);
6✔
744

745
            if (++index == count)
6✔
746
                codeBuilder.AppendLine();
2✔
747
            else
748
                codeBuilder.AppendLine(",");
4✔
749
        }
750

751
        codeBuilder
2✔
752
            .DecrementIndent()
2✔
753
            .AppendLine(");"); // new
2✔
754
    }
2✔
755

756
    private static void WriteReturnObjectInitializer(IndentedStringBuilder codeBuilder, EntityClass entity)
757
    {
758
        codeBuilder
8✔
759
            .Append("return new ")
8✔
760
            .AppendLine(entity.FullyQualified)
8✔
761
            .AppendLine("{")
8✔
762
            .IncrementIndent();
8✔
763

764
        var mappedProperties = entity.Properties.Where(p => !p.IsNotMapped).ToList();
8✔
765
        var index = 0;
8✔
766
        var count = mappedProperties.Count;
8✔
767

768
        foreach (var entityProperty in mappedProperties)
58✔
769
        {
770
            var fieldName = CamelCase(entityProperty.PropertyName);
21✔
771
            codeBuilder
21✔
772
                .Append(entityProperty.PropertyName)
21✔
773
                .Append(" = ")
21✔
774
                .Append(" v_")
21✔
775
                .Append(fieldName);
21✔
776

777
            if (++index == count)
21✔
778
                codeBuilder.AppendLine();
8✔
779
            else
780
                codeBuilder.AppendLine(",");
13✔
781
        }
782

783
        codeBuilder
8✔
784
            .DecrementIndent()
8✔
785
            .AppendLine("};"); // new
8✔
786
    }
8✔
787

788
    private static string GetAliasMap(string type)
789
    {
790
        return type switch
29!
791
        {
29✔
792
            "global::System.Boolean" => "bool",
1✔
793
            "global::System.Byte" => "byte",
×
794
            "global::System.Byte[]" => "byte[]",
1✔
795
            "global::System.Char" => "char",
×
796
            "global::System.Decimal" => "decimal",
×
797
            "global::System.Double" => "double",
×
798
            "global::System.Single" => "float",
×
799
            "global::System.Int16" => "short",
2✔
800
            "global::System.Int32" => "int",
3✔
801
            "global::System.Int64" => "long",
×
802
            "global::System.String" => "string",
2✔
803
            "System.Boolean" => "bool",
×
804
            "System.Byte" => "byte",
×
805
            "System.Byte[]" => "byte[]",
×
806
            "System.Char" => "char",
×
807
            "System.Decimal" => "decimal",
×
808
            "System.Double" => "double",
×
809
            "System.Single" => "float",
×
810
            "System.Int16" => "short",
×
811
            "System.Int32" => "int",
×
812
            "System.Int64" => "long",
×
813
            "System.String" => "string",
×
814
            _ => type,
20✔
815
        };
29✔
816
    }
817

818
    private static string GetReaderName(EntityProperty entityProperty)
819
    {
820
        var propertyType = entityProperty.IsNullable
21!
821
            ? entityProperty.PropertyType.Substring(0, entityProperty.PropertyType.Length - 1)
21✔
822
            : entityProperty.PropertyType;
21✔
823

824
        return GetReaderName(propertyType);
21✔
825
    }
826

827
    private static string GetReaderName(string propertyType)
828
    {
829
        return propertyType switch
22!
830
        {
22✔
831
            "global::System.Boolean" => "GetBoolean",
1✔
832
            "global::System.Byte" => "GetByte",
×
833
            "global::System.Byte[]" => "GetBytes",
1✔
834
            "global::System.Char" => "GetChar",
×
835
            "global::System.DateTime" => "GetDateTime",
×
836
            "global::System.DateTimeOffset" => "GetDateTimeOffset",
2✔
837
            "global::System.Decimal" => "GetDecimal",
×
838
            "global::System.Double" => "GetDouble",
×
839
            "global::System.Guid" => "GetGuid",
×
840
            "global::System.Single" => "GetFloat",
×
841
            "global::System.Int16" => "GetInt16",
×
842
            "global::System.Int32" => "GetInt32",
3✔
843
            "global::System.Int64" => "GetInt64",
×
844
            "global::System.String" => "GetString",
2✔
845
            "System.Boolean" => "GetBoolean",
×
846
            "System.Byte" => "GetByte",
×
847
            "System.Byte[]" => "GetBytes",
×
848
            "System.Char" => "GetChar",
×
849
            "System.DateTime" => "GetDateTime",
×
850
            "System.DateTimeOffset" => "GetDateTimeOffset",
×
851
            "System.Decimal" => "GetDecimal",
×
852
            "System.Double" => "GetDouble",
×
853
            "System.Guid" => "GetGuid",
×
854
            "System.Single" => "GetFloat",
×
855
            "System.Int16" => "GetInt16",
×
856
            "System.Int32" => "GetInt32",
×
857
            "System.Int64" => "GetInt64",
×
858
            "System.String" => "GetString",
×
859
            "bool" => "GetBoolean",
1✔
860
            "byte" => "GetByte",
×
861
            "byte[]" => "GetBytes",
×
862
            "char" => "GetChar",
×
863
            "decimal" => "GetDecimal",
×
864
            "double" => "GetDouble",
×
865
            "float" => "GetFloat",
×
866
            "short" => "GetInt16",
1✔
867
            "int" => "GetInt32",
6✔
868
            "long" => "GetInt64",
×
869
            "string" => "GetString",
5✔
870
            // special handling for ConcurrencyToken to use GetBytes
22✔
871
            "global::FluentCommand.ConcurrencyToken" => "GetBytes",
×
872
            "FluentCommand.ConcurrencyToken" => "GetBytes",
×
873
            // fallback to GetValue<T> for unsupported types
22✔
874
            _ => $"GetValue<{propertyType}>"
×
875
        };
22✔
876
    }
877

878
    private static bool HasJsonColumns(EntityClass entityClass)
879
    {
880
        return entityClass.Properties.Any(static p => p.IsJsonColumn && !p.IsNotMapped);
20✔
881
    }
882

883
    private static string CamelCase(string name)
884
    {
885
        if (string.IsNullOrEmpty(name) || !char.IsUpper(name[0]))
81!
886
            return name;
×
887

888
        char[] chars = name.ToCharArray();
81✔
889
        FixCasing(chars);
81✔
890

891
        return new string(chars);
81✔
892
    }
893

894
    private static void FixCasing(Span<char> chars)
895
    {
896
        for (int i = 0; i < chars.Length; i++)
324✔
897
        {
898
            if (i == 1 && !char.IsUpper(chars[i]))
162✔
899
                break;
900

901
            bool hasNext = (i + 1 < chars.Length);
81✔
902

903
            // Stop when next char is already lowercase.
904
            if (i > 0 && hasNext && !char.IsUpper(chars[i + 1]))
81!
905
            {
906
                // If the next char is a space, lowercase current char before exiting.
907
                if (chars[i + 1] == ' ')
×
908
                    chars[i] = char.ToLowerInvariant(chars[i]);
×
909

910
                break;
×
911
            }
912

913
            chars[i] = char.ToLowerInvariant(chars[i]);
81✔
914
        }
915
    }
81✔
916
}
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