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

loresoft / FluentCommand / 26620856256

29 May 2026 05:53AM UTC coverage: 54.913% (-0.9%) from 55.778%
26620856256

push

github

pwelter34
Validate JsonColumn and use fully-qualified types

1412 of 3414 branches covered (41.36%)

Branch coverage included in aggregate %.

13 of 102 new or added lines in 4 files covered. (12.75%)

12 existing lines in 3 files now uncovered.

4478 of 7312 relevant lines covered (61.24%)

306.51 hits per line

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

62.78
/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)
4 !
12
            throw new ArgumentNullException(nameof(entityClass));
×
13

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

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

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

38
        WriteQueryEntity(codeBuilder, entityClass);
4 ✔
39

40
        WriteQuerySingleEntity(codeBuilder, entityClass);
4 ✔
41

42
        WriteQueryEntityTask(codeBuilder, entityClass);
4 ✔
43

44
        WriteQuerySingleEntityTask(codeBuilder, entityClass);
4 ✔
45

46
        WriteEntityFactory(codeBuilder, entityClass);
4 ✔
47

48
        codeBuilder
4 ✔
49
            .DecrementIndent()
4 ✔
50
            .AppendLine("}") // class
4 ✔
51
            .DecrementIndent()
4 ✔
52
            .AppendLine("}"); // namespace
4 ✔
53

54
        return codeBuilder.ToString();
4 ✔
55
    }
56

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

109
    private static void WriteQueryEntityTask(IndentedStringBuilder codeBuilder, EntityClass entity)
110
    {
111
        // public static Task<IEnumerable<Entity>> QueryEntityAsync(this IDataQueryAsync dataQuery) => QueryAsync(dataQuery, EntityFactory);
112
        codeBuilder
4 ✔
113
            .AppendLine("/// <summary>")
4 ✔
114
            .Append("/// Executes the command against the connection and converts the results to <see cref=\"T:")
4 ✔
115
            .Append(entity.FullyQualified)
4 ✔
116
            .AppendLine("\"/> objects.")
4 ✔
117
            .AppendLine("/// </summary>")
4 ✔
118
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQueryAsync\"/> for this extension method.</param>")
4 ✔
119
            .AppendLine("/// <param name=\"cancellationToken\">The cancellation instruction.</param>")
4 ✔
120
            .AppendLine("/// <returns>")
4 ✔
121
            .Append("/// An <see cref=\"T:System.Collections.Generic.IEnumerable`1\" /> of <see cref=\"T:")
4 ✔
122
            .Append(entity.FullyQualified)
4 ✔
123
            .AppendLine("\"/> objects.")
4 ✔
124
            .AppendLine("/// </returns>")
4 ✔
125
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
4 ✔
126
            .Append(ThisAssembly.Product)
4 ✔
127
            .Append("\", \"")
4 ✔
128
            .Append(ThisAssembly.InformationalVersion)
4 ✔
129
            .AppendLine("\")]")
4 ✔
130
            .Append("public static global::System.Threading.Tasks.Task<global::System.Collections.Generic.IEnumerable<")
4 ✔
131
            .Append(entity.FullyQualified)
4 ✔
132
            .AppendLine(">> QueryAsync<TEntity>(")
4 ✔
133
            .IncrementIndent()
4 ✔
134
            .AppendLine("this global::FluentCommand.IDataQueryAsync dataQuery,")
4 ✔
135
            .AppendLine("global::System.Threading.CancellationToken cancellationToken = default)")
4 ✔
136
            .Append("where TEntity : ")
4 ✔
137
            .AppendLine(entity.FullyQualified)
4 ✔
138
            .DecrementIndent()
4 ✔
139
            .AppendLine("{")
4 ✔
140
            .IncrementIndent()
4 ✔
141
            .Append("return dataQuery.QueryAsync<")
4 ✔
142
            .Append(entity.FullyQualified)
4 ✔
143
            .AppendLine(">(")
4 ✔
144
            .IncrementIndent()
4 ✔
145
            .Append("factory: ")
4 ✔
146
            .Append(entity.EntityName)
4 ✔
147
            .Append("DataReaderExtensions.")
4 ✔
148
            .Append(entity.EntityName)
4 ✔
149
            .AppendLine("Factory,")
4 ✔
150
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
4 ✔
151
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult,")
4 ✔
152
            .AppendLine("cancellationToken: cancellationToken);")
4 ✔
153
            .DecrementIndent()
4 ✔
154
            .DecrementIndent()
4 ✔
155
            .AppendLine("}")
4 ✔
156
            .AppendLine();
4 ✔
157
    }
4 ✔
158

159
    private static void WriteQuerySingleEntity(IndentedStringBuilder codeBuilder, EntityClass entity)
160
    {
161
        // public static Entity QuerySingleEntity(this IDataQuery dataQuery) => QuerySingle(dataQuery, EntityFactory);
162
        codeBuilder
4 ✔
163
            .AppendLine("/// <summary>")
4 ✔
164
            .Append("/// Executes the query and returns the first row in the result as a <see cref=\"T:")
4 ✔
165
            .Append(entity.FullyQualified)
4 ✔
166
            .AppendLine("\"/> object.")
4 ✔
167
            .AppendLine("/// </summary>")
4 ✔
168
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQuery\"/> for this extension method.</param>")
4 ✔
169
            .AppendLine("/// <returns>")
4 ✔
170
            .Append("/// A instance of <see cref=\"T:")
4 ✔
171
            .Append(entity.FullyQualified)
4 ✔
172
            .AppendLine("\"/>  if row exists; otherwise null.")
4 ✔
173
            .AppendLine("/// </returns>")
4 ✔
174
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
4 ✔
175
            .Append(ThisAssembly.Product)
4 ✔
176
            .Append("\", \"")
4 ✔
177
            .Append(ThisAssembly.InformationalVersion)
4 ✔
178
            .AppendLine("\")]")
4 ✔
179
            .Append("public static ")
4 ✔
180
            .Append(entity.FullyQualified)
4 ✔
181
            .AppendLine("? QuerySingle<TEntity>(")
4 ✔
182
            .IncrementIndent()
4 ✔
183
            .AppendLine("this global::FluentCommand.IDataQuery dataQuery)")
4 ✔
184
            .Append("where TEntity : ")
4 ✔
185
            .AppendLine(entity.FullyQualified)
4 ✔
186
            .DecrementIndent()
4 ✔
187
            .AppendLine("{")
4 ✔
188
            .IncrementIndent()
4 ✔
189
            .Append("return dataQuery.QuerySingle<")
4 ✔
190
            .Append(entity.FullyQualified)
4 ✔
191
            .AppendLine(">(")
4 ✔
192
            .IncrementIndent()
4 ✔
193
            .Append("factory: ")
4 ✔
194
            .Append(entity.EntityName)
4 ✔
195
            .Append("DataReaderExtensions.")
4 ✔
196
            .Append(entity.EntityName)
4 ✔
197
            .AppendLine("Factory,")
4 ✔
198
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
4 ✔
199
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult |")
4 ✔
200
            .AppendLine("                 global::System.Data.CommandBehavior.SingleRow);")
4 ✔
201
            .DecrementIndent()
4 ✔
202
            .DecrementIndent()
4 ✔
203
            .AppendLine("}")
4 ✔
204
            .AppendLine();
4 ✔
205
    }
4 ✔
206

207
    private static void WriteQueryEntity(IndentedStringBuilder codeBuilder, EntityClass entity)
208
    {
209
        // public static IEnumerable<Entity> QueryEntity(this IDataQuery dataQuery) => Query(dataQuery, EntityFactory);
210
        codeBuilder
4 ✔
211
            .AppendLine("/// <summary>")
4 ✔
212
            .Append("/// Executes the command against the connection and converts the results to <see cref=\"T:")
4 ✔
213
            .Append(entity.FullyQualified)
4 ✔
214
            .AppendLine("\"/> objects.")
4 ✔
215
            .AppendLine("/// </summary>")
4 ✔
216
            .AppendLine("/// <param name=\"dataQuery\">The <see cref=\"T:FluentCommand.IDataQuery\"/> for this extension method.</param>")
4 ✔
217
            .AppendLine("/// <returns>")
4 ✔
218
            .Append("/// An <see cref=\"T:System.Collections.Generic.IEnumerable`1\" /> of <see cref=\"T:")
4 ✔
219
            .Append(entity.FullyQualified)
4 ✔
220
            .AppendLine("\"/> objects.")
4 ✔
221
            .AppendLine("/// </returns>")
4 ✔
222
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
4 ✔
223
            .Append(ThisAssembly.Product)
4 ✔
224
            .Append("\", \"")
4 ✔
225
            .Append(ThisAssembly.InformationalVersion)
4 ✔
226
            .AppendLine("\")]")
4 ✔
227
            .Append("public static global::System.Collections.Generic.IEnumerable<")
4 ✔
228
            .Append(entity.FullyQualified)
4 ✔
229
            .AppendLine("> Query<TEntity>(")
4 ✔
230
            .IncrementIndent()
4 ✔
231
            .AppendLine("this global::FluentCommand.IDataQuery dataQuery)")
4 ✔
232
            .Append("where TEntity : ")
4 ✔
233
            .AppendLine(entity.FullyQualified)
4 ✔
234
            .DecrementIndent()
4 ✔
235
            .AppendLine("{")
4 ✔
236
            .IncrementIndent()
4 ✔
237
            .Append("return dataQuery.Query<")
4 ✔
238
            .Append(entity.FullyQualified)
4 ✔
239
            .AppendLine(">(")
4 ✔
240
            .IncrementIndent()
4 ✔
241
            .Append("factory: ")
4 ✔
242
            .Append(entity.EntityName)
4 ✔
243
            .Append("DataReaderExtensions.")
4 ✔
244
            .Append(entity.EntityName)
4 ✔
245
            .AppendLine("Factory,")
4 ✔
246
            .AppendLine("commandBehavior: global::System.Data.CommandBehavior.SequentialAccess |")
4 ✔
247
            .AppendLine("                 global::System.Data.CommandBehavior.SingleResult);")
4 ✔
248
            .DecrementIndent()
4 ✔
249
            .DecrementIndent()
4 ✔
250
            .AppendLine("}")
4 ✔
251
            .AppendLine();
4 ✔
252
    }
4 ✔
253

254
    private static void WriteEntityFactory(IndentedStringBuilder codeBuilder, EntityClass entity)
255
    {
256
        codeBuilder
4 ✔
257
            .AppendLine("/// <summary>")
4 ✔
258
            .Append("/// A factory for creating <see cref=\"T:")
4 ✔
259
            .Append(entity.FullyQualified)
4 ✔
260
            .AppendLine("\"/> objects from the current row in the specified <paramref name=\"dataRecord\"/>.")
4 ✔
261
            .AppendLine("/// </summary>")
4 ✔
262
            .AppendLine("/// <param name=\"dataRecord\">The open <see cref=\"T:System.Data.IDataReader\"/> to get the object from.</param>")
4 ✔
263
            .AppendLine("/// <returns>")
4 ✔
264
            .Append("/// A instance of <see cref=\"")
4 ✔
265
            .Append(entity.FullyQualified)
4 ✔
266
            .AppendLine("\"/>  having property names set that match the field names in the <paramref name=\"dataRecord\"/>.")
4 ✔
267
            .AppendLine("/// </returns>")
4 ✔
268
            .Append("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"")
4 ✔
269
            .Append(ThisAssembly.Product)
4 ✔
270
            .Append("\", \"")
4 ✔
271
            .Append(ThisAssembly.InformationalVersion)
4 ✔
272
            .AppendLine("\")]")
4 ✔
273
            .Append("public static ")
4 ✔
274
            .Append(entity.FullyQualified)
4 ✔
275
            .Append(" ")
4 ✔
276
            .Append(entity.EntityName)
4 ✔
277
            .AppendLine("Factory(this global::System.Data.IDataReader dataRecord)")
4 ✔
278
            .AppendLine("{")
4 ✔
279
            .IncrementIndent()
4 ✔
280
            .AppendLine("if (dataRecord == null)")
4 ✔
281
            .IncrementIndent()
4 ✔
282
            .AppendLine("throw new global::System.ArgumentNullException(nameof(dataRecord));")
4 ✔
283
            .DecrementIndent()
4 ✔
284
            .AppendLine();
4 ✔
285

286
        foreach (var entityProperty in entity.Properties)
34 ✔
287
        {
288
            if (entityProperty.IsNotMapped)
13 !
289
                continue;
290

291
            var aliasType = GetAliasMap(entityProperty.PropertyType);
13 ✔
292
            var fieldName = CamelCase(entityProperty.PropertyName);
13 ✔
293

294
            // local variable
295
            codeBuilder
13 ✔
296
                .Append(aliasType)
13 ✔
297
                .Append(" v_")
13 ✔
298
                .Append(fieldName)
13 ✔
299
                .Append(" = default")
13 ✔
300
                .AppendIf("!", _ => !entityProperty.IsNullable)
13 ✔
301
                .AppendLine(";");
13 ✔
302
        }
303

304
        codeBuilder
4 ✔
305
            .AppendLine()
4 ✔
306
            .AppendLine("for (var __index = 0; __index < dataRecord.FieldCount; __index++)")
4 ✔
307
            .AppendLine("{")
4 ✔
308
            .IncrementIndent()
4 ✔
309
            .AppendLine("if (dataRecord.IsDBNull(__index))")
4 ✔
310
            .IncrementIndent()
4 ✔
311
            .AppendLine(" continue;")
4 ✔
312
            .DecrementIndent()
4 ✔
313
            .AppendLine();
4 ✔
314

315
        codeBuilder
4 ✔
316
            .AppendLine("var __name = dataRecord.GetName(__index);")
4 ✔
317
            .AppendLine("switch (__name)")
4 ✔
318
            .AppendLine("{")
4 ✔
319
            .IncrementIndent();
4 ✔
320

321
        foreach (var entityProperty in entity.Properties)
34 ✔
322
        {
323
            if (entityProperty.IsNotMapped)
13 !
324
                continue;
325

326
            var fieldName = CamelCase(entityProperty.PropertyName);
13 ✔
327

328
            codeBuilder
13 ✔
329
                .Append("case \"")
13 ✔
330
                .Append(entityProperty.ColumnName)
13 ✔
331
                .AppendLine("\":");
13 ✔
332

333
            if (entityProperty.IsJsonColumn)
13 ✔
334
            {
335
                var jsonReaderName = entityProperty.IsNullable
4 ✔
336
                    ? "GetFromJson"
4 ✔
337
                    : "GetRequiredFromJson";
4 ✔
338

339
                codeBuilder
4 ✔
340
                    .IncrementIndent()
4 ✔
341
                    .Append("v_")
4 ✔
342
                    .Append(fieldName)
4 ✔
343
                    .Append(" = dataRecord.")
4 ✔
344
                    .Append(jsonReaderName)
4 ✔
345
                    .Append("<")
4 ✔
346
                    .Append(entityProperty.PropertyType)
4 ✔
347
                    .Append(">(__index")
4 ✔
348
                    .Append(GetJsonReaderArgument(entityProperty))
4 ✔
349
                    .AppendLine(");")
4 ✔
350
                    .AppendLine("break;")
4 ✔
351
                    .DecrementIndent();
4 ✔
352
            }
353
            else if (entityProperty.IsEnum)
9 ✔
354
            {
355
                WriteEnumColumnReader(codeBuilder, entityProperty, fieldName);
2 ✔
356
            }
357
            else if (string.IsNullOrEmpty(entityProperty.ConverterName))
7 ✔
358
            {
359
                var readerName = GetReaderName(entityProperty);
6 ✔
360

361
                codeBuilder
6 ✔
362
                    .IncrementIndent()
6 ✔
363
                    .Append("v_")
6 ✔
364
                    .Append(fieldName)
6 ✔
365
                    .Append(" = dataRecord.")
6 ✔
366
                    .Append(readerName)
6 ✔
367
                    .AppendLine("(__index);")
6 ✔
368
                    .AppendLine("break;")
6 ✔
369
                    .DecrementIndent();
6 ✔
370
            }
371
            else
372
            {
373
                // create converter via singleton helper
374
                codeBuilder
1 ✔
375
                    .IncrementIndent()
1 ✔
376
                    .Append("var c_")
1 ✔
377
                    .Append(fieldName)
1 ✔
378
                    .Append(" = global::FluentCommand.Internal.Singleton<")
1 ✔
379
                    .Append(entityProperty.ConverterName!)
1 ✔
380
                    .AppendLine(">.Current")
1 ✔
381
                    .IncrementIndent()
1 ✔
382
                    .Append("as global::FluentCommand.IDataFieldConverter<")
1 ✔
383
                    .Append(entityProperty.PropertyType)
1 ✔
384
                    .AppendLine(">;")
1 ✔
385
                    .DecrementIndent()
1 ✔
386
                    .AppendLine();
1 ✔
387

388
                // read via converter instance
389
                codeBuilder
1 ✔
390
                    .Append("v_")
1 ✔
391
                    .Append(fieldName)
1 ✔
392
                    .Append(" = c_")
1 ✔
393
                    .Append(fieldName)
1 ✔
394
                    .AppendLine(".ReadValue(dataRecord, __index);")
1 ✔
395
                    .AppendLine("break;")
1 ✔
396
                    .DecrementIndent();
1 ✔
397
            }
398
        }
399

400
        codeBuilder
4 ✔
401
            .DecrementIndent()
4 ✔
402
            .AppendLine("}") // switch
4 ✔
403
            .DecrementIndent()
4 ✔
404
            .AppendLine("}") // for
4 ✔
405
            .AppendLine();
4 ✔
406

407
        if (entity.InitializationMode == InitializationMode.Constructor)
4 !
408
            WriteReturnConstructor(codeBuilder, entity);
×
409
        else
410
            WriteReturnObjectInitializer(codeBuilder, entity);
4 ✔
411

412
        codeBuilder
4 ✔
413
            .DecrementIndent()
4 ✔
414
            .AppendLine("}") // method
4 ✔
415
            .AppendLine();
4 ✔
416
    }
4 ✔
417

418
    private static void WriteEnumColumnReader(IndentedStringBuilder codeBuilder, EntityProperty entityProperty, string fieldName)
419
    {
420
        codeBuilder
2 ✔
421
            .IncrementIndent()
2 ✔
422
            .Append("v_")
2 ✔
423
            .Append(fieldName)
2 ✔
424
            .Append(" = ")
2 ✔
425
            .Append(GetEnumReadExpression(entityProperty))
2 ✔
426
            .AppendLine(";")
2 ✔
427
            .AppendLine("break;")
2 ✔
428
            .DecrementIndent();
2 ✔
429
    }
2 ✔
430

431
    private static string GetEnumReadExpression(EntityProperty entityProperty)
432
    {
433
        var underlyingType = GetAliasMap(entityProperty.EnumUnderlyingType ?? "int");
2 !
434

435
        if (entityProperty.IsNullableEnum)
2 ✔
436
            return $"({entityProperty.PropertyType})dataRecord.GetValue<{underlyingType}?>(__index)";
1 ✔
437

438
        return $"({entityProperty.PropertyType})dataRecord.{GetReaderName(underlyingType)}(__index)";
1 ✔
439
    }
440

441
    private static void WriteReturnConstructor(IndentedStringBuilder codeBuilder, EntityClass entity)
442
    {
443
        codeBuilder
×
444
            .Append("return new ")
×
445
            .Append(entity.FullyQualified)
×
446
            .AppendLine("(")
×
447
            .IncrementIndent();
×
448

449
        var mappedProperties = entity.Properties.Where(p => !p.IsNotMapped).ToList();
×
450
        var index = 0;
×
451
        var count = mappedProperties.Count;
×
452

453
        foreach (var entityProperty in mappedProperties)
×
454
        {
455
            var fieldName = CamelCase(entityProperty.PropertyName);
×
456
            codeBuilder
×
457
                .Append(entityProperty.ParameterName!)
×
458
                .Append(": ")
×
459
                .Append(" v_")
×
460
                .Append(fieldName);
×
461

462
            if (++index == count)
×
463
                codeBuilder.AppendLine();
×
464
            else
465
                codeBuilder.AppendLine(",");
×
466
        }
467

468
        codeBuilder
×
469
            .DecrementIndent()
×
470
            .AppendLine(");"); // new
×
471
    }
×
472

473
    private static void WriteReturnObjectInitializer(IndentedStringBuilder codeBuilder, EntityClass entity)
474
    {
475
        codeBuilder
4 ✔
476
            .Append("return new ")
4 ✔
477
            .AppendLine(entity.FullyQualified)
4 ✔
478
            .AppendLine("{")
4 ✔
479
            .IncrementIndent();
4 ✔
480

481
        var mappedProperties = entity.Properties.Where(p => !p.IsNotMapped).ToList();
4 ✔
482
        var index = 0;
4 ✔
483
        var count = mappedProperties.Count;
4 ✔
484

485
        foreach (var entityProperty in mappedProperties)
34 ✔
486
        {
487
            var fieldName = CamelCase(entityProperty.PropertyName);
13 ✔
488
            codeBuilder
13 ✔
489
                .Append(entityProperty.PropertyName)
13 ✔
490
                .Append(" = ")
13 ✔
491
                .Append(" v_")
13 ✔
492
                .Append(fieldName);
13 ✔
493

494
            if (++index == count)
13 ✔
495
                codeBuilder.AppendLine();
4 ✔
496
            else
497
                codeBuilder.AppendLine(",");
9 ✔
498
        }
499

500
        codeBuilder
4 ✔
501
            .DecrementIndent()
4 ✔
502
            .AppendLine("};"); // new
4 ✔
503
    }
4 ✔
504

505
    private static string GetAliasMap(string type)
506
    {
507
        return type switch
15 !
508
        {
15 ✔
509
            "global::System.Boolean" => "bool",
1 ✔
NEW
510
            "global::System.Byte" => "byte",
×
511
            "global::System.Byte[]" => "byte[]",
1 ✔
NEW
512
            "global::System.Char" => "char",
×
NEW
513
            "global::System.Decimal" => "decimal",
×
NEW
514
            "global::System.Double" => "double",
×
NEW
515
            "global::System.Single" => "float",
×
516
            "global::System.Int16" => "short",
2 ✔
517
            "global::System.Int32" => "int",
2 ✔
NEW
518
            "global::System.Int64" => "long",
×
519
            "global::System.String" => "string",
1 ✔
520
            "System.Boolean" => "bool",
×
521
            "System.Byte" => "byte",
×
UNCOV
522
            "System.Byte[]" => "byte[]",
×
523
            "System.Char" => "char",
×
524
            "System.Decimal" => "decimal",
×
525
            "System.Double" => "double",
×
526
            "System.Single" => "float",
×
527
            "System.Int16" => "short",
×
UNCOV
528
            "System.Int32" => "int",
×
529
            "System.Int64" => "long",
×
UNCOV
530
            "System.String" => "string",
×
531
            _ => type,
8 ✔
532
        };
15 ✔
533
    }
534

535
    private static string GetReaderName(EntityProperty entityProperty)
536
    {
537
        var propertyType = entityProperty.IsNullable
6 !
538
            ? entityProperty.PropertyType.Substring(0, entityProperty.PropertyType.Length - 1)
6 ✔
539
            : entityProperty.PropertyType;
6 ✔
540

541
        return GetReaderName(propertyType);
6 ✔
542
    }
543

544
    private static string GetReaderName(string propertyType)
545
    {
546
        return propertyType switch
7 !
547
        {
7 ✔
548
            "global::System.Boolean" => "GetBoolean",
1 ✔
NEW
549
            "global::System.Byte" => "GetByte",
×
550
            "global::System.Byte[]" => "GetBytes",
1 ✔
NEW
551
            "global::System.Char" => "GetChar",
×
NEW
552
            "global::System.DateTime" => "GetDateTime",
×
553
            "global::System.DateTimeOffset" => "GetDateTimeOffset",
1 ✔
NEW
554
            "global::System.Decimal" => "GetDecimal",
×
NEW
555
            "global::System.Double" => "GetDouble",
×
NEW
556
            "global::System.Guid" => "GetGuid",
×
NEW
557
            "global::System.Single" => "GetFloat",
×
NEW
558
            "global::System.Int16" => "GetInt16",
×
559
            "global::System.Int32" => "GetInt32",
2 ✔
NEW
560
            "global::System.Int64" => "GetInt64",
×
561
            "global::System.String" => "GetString",
1 ✔
562
            "System.Boolean" => "GetBoolean",
×
563
            "System.Byte" => "GetByte",
×
UNCOV
564
            "System.Byte[]" => "GetBytes",
×
565
            "System.Char" => "GetChar",
×
566
            "System.DateTime" => "GetDateTime",
×
UNCOV
567
            "System.DateTimeOffset" => "GetDateTimeOffset",
×
568
            "System.Decimal" => "GetDecimal",
×
569
            "System.Double" => "GetDouble",
×
570
            "System.Guid" => "GetGuid",
×
571
            "System.Single" => "GetFloat",
×
572
            "System.Int16" => "GetInt16",
×
UNCOV
573
            "System.Int32" => "GetInt32",
×
574
            "System.Int64" => "GetInt64",
×
UNCOV
575
            "System.String" => "GetString",
×
576
            "bool" => "GetBoolean",
×
577
            "byte" => "GetByte",
×
578
            "byte[]" => "GetBytes",
×
579
            "char" => "GetChar",
×
580
            "decimal" => "GetDecimal",
×
581
            "double" => "GetDouble",
×
582
            "float" => "GetFloat",
×
583
            "short" => "GetInt16",
1 ✔
UNCOV
584
            "int" => "GetInt32",
×
585
            "long" => "GetInt64",
×
586
            "string" => "GetString",
×
587
            // special handling for ConcurrencyToken to use GetBytes
7 ✔
NEW
588
            "global::FluentCommand.ConcurrencyToken" => "GetBytes",
×
589
            "FluentCommand.ConcurrencyToken" => "GetBytes",
×
590
            // fallback to GetValue<T> for unsupported types
7 ✔
UNCOV
591
            _ => $"GetValue<{propertyType}>"
×
592
        };
7 ✔
593
    }
594

595
    private static string GetJsonReaderArgument(EntityProperty entityProperty)
596
    {
597
        if (!string.IsNullOrEmpty(entityProperty.JsonOptionsProviderName))
4 ✔
598
            return $", {entityProperty.JsonOptionsProviderName}.Options";
1 ✔
599

600
        if (!string.IsNullOrEmpty(entityProperty.JsonContextName)
3 ✔
601
            && !string.IsNullOrEmpty(entityProperty.JsonTypeInfoPropertyName))
3 ✔
602
        {
603
            return $", {entityProperty.JsonContextName}.Default.{entityProperty.JsonTypeInfoPropertyName}";
1 ✔
604
        }
605

606
        return string.Empty;
2 ✔
607
    }
608

609
    private static string CamelCase(string name)
610
    {
611
        if (string.IsNullOrEmpty(name) || !char.IsUpper(name[0]))
39 !
612
            return name;
×
613

614
        char[] chars = name.ToCharArray();
39 ✔
615
        FixCasing(chars);
39 ✔
616

617
        return new string(chars);
39 ✔
618
    }
619

620
    private static void FixCasing(Span<char> chars)
621
    {
622
        for (int i = 0; i < chars.Length; i++)
156 ✔
623
        {
624
            if (i == 1 && !char.IsUpper(chars[i]))
78 ✔
625
                break;
626

627
            bool hasNext = (i + 1 < chars.Length);
39 ✔
628

629
            // Stop when next char is already lowercase.
630
            if (i > 0 && hasNext && !char.IsUpper(chars[i + 1]))
39 !
631
            {
632
                // If the next char is a space, lowercase current char before exiting.
633
                if (chars[i + 1] == ' ')
×
634
                    chars[i] = char.ToLowerInvariant(chars[i]);
×
635

636
                break;
×
637
            }
638

639
            chars[i] = char.ToLowerInvariant(chars[i]);
39 ✔
640
        }
641
    }
39 ✔
642
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc