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

loresoft / FluentCommand / 26594173245

28 May 2026 06:28PM UTC coverage: 55.553% (+0.7%) from 54.902%
26594173245

push

github

pwelter34
Move JSON support, add docs and examples

1358 of 3215 branches covered (42.24%)

Branch coverage included in aggregate %.

103 of 234 new or added lines in 9 files covered. (44.02%)

371 existing lines in 26 files now uncovered.

4389 of 7130 relevant lines covered (61.56%)

312.89 hits per line

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

62.58
/src/FluentCommand/Query/SelectEntityBuilder.cs
1
using System.Linq.Expressions;
2

3
using FluentCommand.Extensions;
4
using FluentCommand.Query.Generators;
5
using FluentCommand.Reflection;
6

7
namespace FluentCommand.Query;
8

9
/// <summary>
10
/// Provides a builder for constructing SQL SELECT statements for a specific entity type with fluent, chainable methods.
11
/// </summary>
12
/// <typeparam name="TEntity">The type of the entity.</typeparam>
13
public class SelectEntityBuilder<TEntity>
14
    : SelectBuilder<SelectEntityBuilder<TEntity>>, IWhereEntityBuilder<TEntity, SelectEntityBuilder<TEntity>>
15
    where TEntity : class
16
{
17
    private static readonly TypeAccessor _typeAccessor = TypeAccessor.GetAccessor<TEntity>();
12✔
18

19
    /// <summary>
20
    /// Initializes a new instance of the <see cref="SelectEntityBuilder{TEntity}"/> class.
21
    /// </summary>
22
    /// <param name="queryGenerator">The <see cref="IQueryGenerator"/> used to generate SQL expressions.</param>
23
    /// <param name="parameters">The list of <see cref="QueryParameter"/> objects for the query.</param>
24
    /// <param name="logicalOperator">The logical operator (<see cref="LogicalOperators"/>) to combine WHERE expressions. Defaults to <see cref="LogicalOperators.And"/>.</param>
25
    public SelectEntityBuilder(IQueryGenerator queryGenerator, List<QueryParameter> parameters, LogicalOperators logicalOperator = LogicalOperators.And)
26
        : base(queryGenerator, parameters, logicalOperator)
53✔
27
    {
28
    }
53✔
29

30
    /// <summary>
31
    /// Adds a column expression for the specified entity property.
32
    /// </summary>
33
    /// <param name="property">An expression selecting the property to include in the SELECT clause.</param>
34
    /// <param name="tableAlias">The alias of the table (optional).</param>
35
    /// <param name="columnAlias">The alias for the column (optional).</param>
36
    /// <returns>
37
    /// The same builder instance for method chaining.
38
    /// </returns>
39
    public SelectEntityBuilder<TEntity> Column(
40
        Expression<Func<TEntity, object?>> property,
41
        string? tableAlias = null,
42
        string? columnAlias = null)
43
    {
44
        var propertyAccessor = GetPropertyAccessor(property);
26✔
45

46
        // alias column as property name if they don't match
47
        if (propertyAccessor.Name == propertyAccessor.Column)
26✔
48
            return Column(propertyAccessor.Column, tableAlias, columnAlias);
25✔
49
        else
50
            return Column(propertyAccessor.Column, tableAlias, columnAlias ?? propertyAccessor.Name);
1!
51
    }
52

53
    /// <summary>
54
    /// Adds a column expression for the specified property of a model type.
55
    /// </summary>
56
    /// <typeparam name="TModel">The type of the model.</typeparam>
57
    /// <param name="property">An expression selecting the property to include in the SELECT clause.</param>
58
    /// <param name="tableAlias">The alias of the table (optional).</param>
59
    /// <param name="columnAlias">The alias for the column (optional).</param>
60
    /// <returns>
61
    /// The same builder instance for method chaining.
62
    /// </returns>
63
    public SelectEntityBuilder<TEntity> Column<TModel>(
64
        Expression<Func<TModel, object?>> property,
65
        string? tableAlias = null,
66
        string? columnAlias = null) where TModel : class
67
    {
68
        var typeAccessor = TypeAccessor.GetAccessor<TModel>();
6✔
69
        var propertyAccessor = GetPropertyAccessor(typeAccessor, property);
6✔
70

71
        // alias column as property name if they don't match
72
        if (propertyAccessor.Name == propertyAccessor.Column)
6!
73
            return Column(propertyAccessor.Column, tableAlias, columnAlias);
6✔
74
        else
75
            return Column(propertyAccessor.Column, tableAlias, columnAlias ?? propertyAccessor.Name);
×
76
    }
77

78
    /// <summary>
79
    /// Conditionally adds a column expression for the specified entity property.
80
    /// </summary>
81
    /// <typeparam name="TValue">The type of the property value.</typeparam>
82
    /// <param name="property">An expression selecting the property to include in the SELECT clause.</param>
83
    /// <param name="tableAlias">The alias of the table (optional).</param>
84
    /// <param name="columnAlias">The alias for the column (optional).</param>
85
    /// <param name="condition">A function that determines whether to add the column, based on the column name. If <c>null</c>, the column is always added.</param>
86
    /// <returns>
87
    /// The same builder instance for method chaining.
88
    /// </returns>
89
    public SelectEntityBuilder<TEntity> ColumnIf<TValue>(
90
        Expression<Func<TEntity, TValue>> property,
91
        string? tableAlias = null,
92
        string? columnAlias = null,
93
        Func<string, bool>? condition = null)
94
    {
95
        var propertyAccessor = GetPropertyAccessor(property);
×
96

97
        // alias column as property name if they don't match
98
        if (propertyAccessor.Name == propertyAccessor.Column)
×
99
            return ColumnIf(propertyAccessor.Column, tableAlias, columnAlias, condition);
×
100
        else
101
            return ColumnIf(propertyAccessor.Column, tableAlias, columnAlias ?? propertyAccessor.Name, condition);
×
102
    }
103

104
    /// <summary>
105
    /// Adds a column expression for each of the specified column names, using entity property mapping.
106
    /// </summary>
107
    /// <param name="columnNames">The collection of column names to include in the SELECT clause.</param>
108
    /// <param name="tableAlias">The alias of the table (optional).</param>
109
    /// <returns>
110
    /// The same builder instance for method chaining.
111
    /// </returns>
112
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="columnNames"/> is <c>null</c>.</exception>
113
    public override SelectEntityBuilder<TEntity> Columns(
114
        IEnumerable<string> columnNames,
115
        string? tableAlias = null)
116
    {
117
        ArgumentNullException.ThrowIfNull(columnNames);
1✔
118

119
        foreach (var column in columnNames)
6✔
120
        {
121
            var propertyAccessor = _typeAccessor.FindColumn(column);
2✔
122
            if (propertyAccessor is null)
2✔
123
                continue;
124

125
            // alias column as property name if they don't match
126
            if (propertyAccessor.Name == propertyAccessor.Column)
2✔
127
                Column(propertyAccessor.Column, tableAlias);
1✔
128
            else
129
                Column(propertyAccessor.Column, tableAlias, propertyAccessor.Name);
1✔
130
        }
131

132
        return this;
1✔
133
    }
134

135
    /// <summary>
136
    /// Adds a column expression for each property in <typeparamref name="TEntity"/>.
137
    /// </summary>
138
    /// <param name="tableAlias">The alias of the table (optional).</param>
139
    /// <param name="filter">An optional filter to include properties.</param>
140
    /// <returns>
141
    /// The same builder instance for method chaining.
142
    /// </returns>
143
    public SelectEntityBuilder<TEntity> Columns(
144
        string? tableAlias = null,
145
        Func<IMemberAccessor, bool>? filter = null)
146
    {
147
        var properties = _typeAccessor.GetProperties();
3✔
148

149
        foreach (var property in properties)
144✔
150
        {
151
            if (property.IsNotMapped)
69✔
152
                continue;
153

154
            if (filter != null && !filter(property))
57✔
155
                continue;
156

157
            // alias column as property name if they don't match
158
            if (property.Name == property.Column)
56!
159
                Column(property.Column, tableAlias);
56✔
160
            else
UNCOV
161
                Column(property.Column, tableAlias, property.Name);
×
162
        }
163

164
        return this;
3✔
165
    }
166

167
    /// <summary>
168
    /// Adds a column expression for each property in <typeparamref name="TModel"/>.
169
    /// </summary>
170
    /// <typeparam name="TModel">The type of the model.</typeparam>
171
    /// <param name="tableAlias">The alias of the table (optional).</param>
172
    /// <param name="filter">An optional filter to include properties.</param>
173
    /// <returns>
174
    /// The same builder instance for method chaining.
175
    /// </returns>
176
    public SelectEntityBuilder<TEntity> Columns<TModel>(
177
        string? tableAlias = null,
178
        Func<IMemberAccessor, bool>? filter = null)
179
    {
UNCOV
180
        var typeAccessor = TypeAccessor.GetAccessor<TModel>();
×
181
        var properties = typeAccessor.GetProperties();
×
182

UNCOV
183
        foreach (var property in properties)
×
184
        {
UNCOV
185
            if (property.IsNotMapped)
×
186
                continue;
187

UNCOV
188
            if (filter != null && !filter(property))
×
189
                continue;
190

191
            // alias column as property name if they don't match
UNCOV
192
            if (property.Name == property.Column)
×
193
                Column(property.Column, tableAlias);
×
194
            else
UNCOV
195
                Column(property.Column, tableAlias, property.Name);
×
196
        }
197

UNCOV
198
        return this;
×
199
    }
200

201
    /// <summary>
202
    /// Adds a COUNT aggregate expression using the specified entity property.
203
    /// </summary>
204
    /// <typeparam name="TValue">The type of the property value.</typeparam>
205
    /// <param name="property">An expression selecting the property to count.</param>
206
    /// <param name="tableAlias">The alias of the table (optional).</param>
207
    /// <param name="columnAlias">The alias for the column (optional).</param>
208
    /// <returns>
209
    /// The same builder instance for method chaining.
210
    /// </returns>
211
    public SelectEntityBuilder<TEntity> Count<TValue>(
212
        Expression<Func<TEntity, TValue>> property,
213
        string? tableAlias = null,
214
        string? columnAlias = null)
215
    {
UNCOV
216
        var propertyAccessor = GetPropertyAccessor(property);
×
217

UNCOV
218
        return Count(propertyAccessor.Column, tableAlias, columnAlias);
×
219
    }
220

221
    /// <summary>
222
    /// Adds an aggregate expression using the specified function and entity property.
223
    /// </summary>
224
    /// <typeparam name="TValue">The type of the property value.</typeparam>
225
    /// <param name="property">An expression selecting the property to aggregate.</param>
226
    /// <param name="function">The aggregate function to use (e.g., <see cref="AggregateFunctions.Sum"/>).</param>
227
    /// <param name="tableAlias">The alias of the table (optional).</param>
228
    /// <param name="columnAlias">The alias for the column (optional).</param>
229
    /// <returns>
230
    /// The same builder instance for method chaining.
231
    /// </returns>
232
    public SelectEntityBuilder<TEntity> Aggregate<TValue>(
233
        Expression<Func<TEntity, TValue>> property,
234
        AggregateFunctions function,
235
        string? tableAlias = null,
236
        string? columnAlias = null)
237
    {
238
        var propertyAccessor = GetPropertyAccessor(property);
3✔
239

240
        return Aggregate(function, propertyAccessor.Column, tableAlias, columnAlias);
3✔
241
    }
242

243
    /// <summary>
244
    /// Sets the target table for the SELECT statement using the entity's mapping information by default.
245
    /// </summary>
246
    /// <param name="tableName">The name of the table (optional, defaults to entity mapping).</param>
247
    /// <param name="tableSchema">The schema of the table (optional, defaults to entity mapping).</param>
248
    /// <param name="tableAlias">The alias of the table (optional).</param>
249
    /// <returns>
250
    /// The same builder instance for method chaining.
251
    /// </returns>
252
    public override SelectEntityBuilder<TEntity> From(
253
        string? tableName = null,
254
        string? tableSchema = null,
255
        string? tableAlias = null)
256
    {
257
        return base.From(
50✔
258
            tableName ?? _typeAccessor.TableName,
50✔
259
            tableSchema ?? _typeAccessor.TableSchema,
50✔
260
            tableAlias);
50✔
261
    }
262

263
    /// <summary>
264
    /// Adds a JOIN clause to the SELECT statement using the specified builder action for the right entity.
265
    /// </summary>
266
    /// <typeparam name="TRight">The type of the right join entity.</typeparam>
267
    /// <param name="builder">An action that configures the join using a <see cref="JoinEntityBuilder{TEntity, TRight}"/>.</param>
268
    /// <returns>
269
    /// The same builder instance for method chaining.
270
    /// </returns>
271
    public SelectEntityBuilder<TEntity> Join<TRight>(Action<JoinEntityBuilder<TEntity, TRight>> builder)
272
        where TRight : class
273
    {
274
        var innerBuilder = new JoinEntityBuilder<TEntity, TRight>(QueryGenerator, Parameters);
7✔
275
        builder(innerBuilder);
7✔
276

277
        JoinExpressions.Add(innerBuilder.BuildExpression());
7✔
278

279
        return this;
7✔
280
    }
281

282
    /// <summary>
283
    /// Adds a JOIN clause to the SELECT statement using the specified builder action for the left and right entities.
284
    /// </summary>
285
    /// <typeparam name="TLeft">The type of the left join entity.</typeparam>
286
    /// <typeparam name="TRight">The type of the right join entity.</typeparam>
287
    /// <param name="builder">An action that configures the join using a <see cref="JoinEntityBuilder{TLeft, TRight}"/>.</param>
288
    /// <returns>
289
    /// The same builder instance for method chaining.
290
    /// </returns>
291
    public SelectEntityBuilder<TEntity> Join<TLeft, TRight>(Action<JoinEntityBuilder<TLeft, TRight>> builder)
292
        where TLeft : class
293
        where TRight : class
294
    {
295
        var innerBuilder = new JoinEntityBuilder<TLeft, TRight>(QueryGenerator, Parameters);
3✔
296
        builder(innerBuilder);
3✔
297

298
        JoinExpressions.Add(innerBuilder.BuildExpression());
3✔
299

300
        return this;
3✔
301
    }
302

303
    /// <inheritdoc />
304
    public SelectEntityBuilder<TEntity> Where<TValue>(
305
        Expression<Func<TEntity, TValue>> property,
306
        TValue? parameterValue,
307
        FilterOperators filterOperator = FilterOperators.Equal)
308
    {
309
        return Where<TValue>(property, parameterValue, null, filterOperator);
20✔
310
    }
311

312
    /// <inheritdoc />
313
    public SelectEntityBuilder<TEntity> Where<TValue>(
314
        Expression<Func<TEntity, TValue>> property,
315
        TValue? parameterValue,
316
        string? tableAlias,
317
        FilterOperators filterOperator = FilterOperators.Equal)
318
    {
319
        var propertyAccessor = GetPropertyAccessor(property);
24✔
320

321
        return Where(propertyAccessor.Column, parameterValue, tableAlias, filterOperator);
24✔
322
    }
323

324
    /// <summary>
325
    /// Adds a WHERE clause for the specified model property, value, filter operator, and table alias.
326
    /// </summary>
327
    /// <typeparam name="TModel">The type of the model.</typeparam>
328
    /// <typeparam name="TValue">The type of the value to compare.</typeparam>
329
    /// <param name="property">An expression selecting the property to filter on.</param>
330
    /// <param name="parameterValue">The value to compare the property against.</param>
331
    /// <param name="tableAlias">The alias of the table (optional).</param>
332
    /// <param name="filterOperator">The filter operator to use (default is <see cref="FilterOperators.Equal"/>).</param>
333
    /// <returns>
334
    /// The same builder instance for method chaining.
335
    /// </returns>
336
    public SelectEntityBuilder<TEntity> Where<TModel, TValue>(
337
        Expression<Func<TModel, TValue>> property,
338
        TValue? parameterValue,
339
        string? tableAlias,
340
        FilterOperators filterOperator = FilterOperators.Equal)
341
    {
342
        var typeAccessor = TypeAccessor.GetAccessor<TModel>();
1✔
343
        var propertyAccessor = GetPropertyAccessor(typeAccessor, property);
1✔
344

345
        return Where(propertyAccessor.Column, parameterValue, tableAlias, filterOperator);
1✔
346
    }
347

348
    /// <inheritdoc />
349
    public SelectEntityBuilder<TEntity> WhereIf<TValue>(
350
        Expression<Func<TEntity, TValue>> property,
351
        TValue? parameterValue,
352
        FilterOperators filterOperator = FilterOperators.Equal,
353
        Func<string, TValue?, bool>? condition = null)
354
    {
UNCOV
355
        return WhereIf(property, parameterValue, null, filterOperator, condition);
×
356
    }
357

358
    /// <inheritdoc />
359
    public SelectEntityBuilder<TEntity> WhereIf<TValue>(
360
        Expression<Func<TEntity, TValue>> property,
361
        TValue? parameterValue,
362
        string? tableAlias,
363
        FilterOperators filterOperator = FilterOperators.Equal,
364
        Func<string, TValue?, bool>? condition = null)
365
    {
UNCOV
366
        var propertyAccessor = GetPropertyAccessor(property);
×
367

UNCOV
368
        return WhereIf(propertyAccessor.Column, parameterValue, tableAlias, filterOperator, condition);
×
369
    }
370

371
    /// <inheritdoc />
372
    public SelectEntityBuilder<TEntity> WhereIn<TValue>(
373
        Expression<Func<TEntity, TValue>> property,
374
        IEnumerable<TValue> parameterValues,
375
        string? tableAlias = null)
376
    {
377
        var propertyAccessor = GetPropertyAccessor(property);
8✔
378

379
        return WhereIn(propertyAccessor.Column, parameterValues, tableAlias);
8✔
380
    }
381

382
    /// <inheritdoc />
383
    public SelectEntityBuilder<TEntity> WhereInIf<TValue>(
384
        Expression<Func<TEntity, TValue>> property,
385
        IEnumerable<TValue> parameterValues,
386
        Func<string, IEnumerable<TValue>, bool>? condition = null)
387
    {
388
        var propertyAccessor = GetPropertyAccessor(property);
1✔
389

390
        return WhereInIf(propertyAccessor.Column, parameterValues, condition);
1✔
391
    }
392

393
    /// <inheritdoc />
394
    public SelectEntityBuilder<TEntity> WhereInIf<TValue>(
395
        Expression<Func<TEntity, TValue>> property,
396
        IEnumerable<TValue> parameterValues,
397
        string? tableAlias,
398
        Func<string, IEnumerable<TValue>, bool>? condition = null)
399
    {
UNCOV
400
        var propertyAccessor = GetPropertyAccessor(property);
×
401

UNCOV
402
        return WhereInIf(propertyAccessor.Column, parameterValues, tableAlias, condition);
×
403
    }
404

405
    /// <inheritdoc />
406
    public SelectEntityBuilder<TEntity> WhereOr(Action<LogicalEntityBuilder<TEntity>> builder)
407
    {
408
        var innerBuilder = new LogicalEntityBuilder<TEntity>(QueryGenerator, Parameters, LogicalOperators.Or);
5✔
409

410
        builder(innerBuilder);
5✔
411

412
        var statement = innerBuilder.BuildStatement();
5✔
413

414
        if (statement != null)
5!
415
            WhereExpressions.Add(new WhereExpression(statement.Statement, IsRaw: true));
5✔
416

417
        return this;
5✔
418
    }
419

420
    /// <inheritdoc />
421
    public SelectEntityBuilder<TEntity> WhereAnd(Action<LogicalEntityBuilder<TEntity>> builder)
422
    {
UNCOV
423
        var innerBuilder = new LogicalEntityBuilder<TEntity>(QueryGenerator, Parameters, LogicalOperators.And);
×
424

UNCOV
425
        builder(innerBuilder);
×
426

UNCOV
427
        var statement = innerBuilder.BuildStatement();
×
428

UNCOV
429
        if (statement != null && statement.Statement.HasValue())
×
430
            WhereExpressions.Add(new WhereExpression(statement.Statement, IsRaw: true));
×
431

UNCOV
432
        return this;
×
433
    }
434

435
    /// <summary>
436
    /// Adds an ORDER BY clause with the specified entity property and sort direction.
437
    /// </summary>
438
    /// <typeparam name="TValue">The type of the property value.</typeparam>
439
    /// <param name="property">An expression selecting the property to sort by.</param>
440
    /// <param name="sortDirection">The sort direction (default is <see cref="SortDirections.Ascending"/>).</param>
441
    /// <returns>
442
    /// The same builder instance for method chaining.
443
    /// </returns>
444
    public SelectEntityBuilder<TEntity> OrderBy<TValue>(
445
        Expression<Func<TEntity, TValue>> property,
446
        SortDirections sortDirection = SortDirections.Ascending)
447
    {
448
        var propertyAccessor = GetPropertyAccessor(property);
26✔
449

450
        return OrderBy(propertyAccessor.Column, null, sortDirection);
26✔
451
    }
452

453
    /// <summary>
454
    /// Adds an ORDER BY clause with the specified entity property, sort direction, and table alias.
455
    /// </summary>
456
    /// <typeparam name="TValue">The type of the property value.</typeparam>
457
    /// <param name="property">An expression selecting the property to sort by.</param>
458
    /// <param name="tableAlias">The alias of the table (optional).</param>
459
    /// <param name="sortDirection">The sort direction (default is <see cref="SortDirections.Ascending"/>).</param>
460
    /// <returns>
461
    /// The same builder instance for method chaining.
462
    /// </returns>
463
    public SelectEntityBuilder<TEntity> OrderBy<TValue>(
464
        Expression<Func<TEntity, TValue>> property,
465
        string? tableAlias,
466
        SortDirections sortDirection = SortDirections.Ascending)
467
    {
468
        var propertyAccessor = GetPropertyAccessor(property);
4✔
469

470
        return OrderBy(propertyAccessor.Column, tableAlias, sortDirection);
4✔
471
    }
472

473
    /// <summary>
474
    /// Conditionally adds an ORDER BY clause with the specified entity property and sort direction.
475
    /// </summary>
476
    /// <typeparam name="TValue">The type of the property value.</typeparam>
477
    /// <param name="property">An expression selecting the property to sort by.</param>
478
    /// <param name="sortDirection">The sort direction (default is <see cref="SortDirections.Ascending"/>).</param>
479
    /// <param name="condition">A function that determines whether to add the ORDER BY clause, based on the property name. If <c>null</c>, the clause is always added.</param>
480
    /// <returns>
481
    /// The same builder instance for method chaining.
482
    /// </returns>
483
    public SelectEntityBuilder<TEntity> OrderByIf<TValue>(
484
        Expression<Func<TEntity, TValue>> property,
485
        SortDirections sortDirection = SortDirections.Ascending,
486
        Func<string, bool>? condition = null)
487
    {
UNCOV
488
        var propertyAccessor = GetPropertyAccessor(property);
×
489

UNCOV
490
        return OrderByIf(propertyAccessor.Column, null, sortDirection, condition);
×
491
    }
492

493
    /// <summary>
494
    /// Conditionally adds an ORDER BY clause with the specified entity property, sort direction, and table alias.
495
    /// </summary>
496
    /// <typeparam name="TValue">The type of the property value.</typeparam>
497
    /// <param name="property">An expression selecting the property to sort by.</param>
498
    /// <param name="tableAlias">The alias of the table (optional).</param>
499
    /// <param name="sortDirection">The sort direction (default is <see cref="SortDirections.Ascending"/>).</param>
500
    /// <param name="condition">A function that determines whether to add the ORDER BY clause, based on the property name. If <c>null</c>, the clause is always added.</param>
501
    /// <returns>
502
    /// The same builder instance for method chaining.
503
    /// </returns>
504
    public SelectEntityBuilder<TEntity> OrderByIf<TValue>(
505
        Expression<Func<TEntity, TValue>> property,
506
        string? tableAlias,
507
        SortDirections sortDirection = SortDirections.Ascending,
508
        Func<string, bool>? condition = null)
509
    {
UNCOV
510
        var propertyAccessor = GetPropertyAccessor(property);
×
511

UNCOV
512
        return OrderByIf(propertyAccessor.Column, tableAlias, sortDirection, condition);
×
513
    }
514

515
    /// <summary>
516
    /// Adds a GROUP BY clause with the specified entity property and table alias.
517
    /// </summary>
518
    /// <typeparam name="TValue">The type of the property value.</typeparam>
519
    /// <param name="property">An expression selecting the property to group by.</param>
520
    /// <param name="tableAlias">The alias of the table (optional).</param>
521
    /// <returns>
522
    /// The same builder instance for method chaining.
523
    /// </returns>
524
    public SelectEntityBuilder<TEntity> GroupBy<TValue>(
525
        Expression<Func<TEntity, TValue>> property,
526
        string? tableAlias = null)
527
    {
528
        var propertyAccessor = GetPropertyAccessor(property);
3✔
529

530
        return GroupBy(propertyAccessor.Column, tableAlias);
3✔
531
    }
532

533
    /// <summary>
534
    /// Builds the SQL SELECT statement using the current configuration.
535
    /// </summary>
536
    /// <returns>
537
    /// A <see cref="QueryStatement"/> containing the SQL SELECT statement and its parameters.
538
    /// </returns>
539
    public override QueryStatement? BuildStatement()
540
    {
541
        // add table and schema from attribute if not set
542
        if (FromExpressions.Count == 0)
53✔
543
            From(_typeAccessor.TableName, _typeAccessor.TableSchema);
45✔
544

545
        return base.BuildStatement();
53✔
546
    }
547

548
    private static IMemberAccessor GetPropertyAccessor<TModel, TValue>(
549
        TypeAccessor typeAccessor,
550
        Expression<Func<TModel, TValue>> property)
551
    {
552
        ArgumentNullException.ThrowIfNull(property);
102✔
553

554
        var propertyAccessor = typeAccessor.FindProperty(property);
102✔
555
        if (propertyAccessor is null)
102!
UNCOV
556
            throw new ArgumentException("The specified property does not exist on the entity.", nameof(property));
×
557

558
        return propertyAccessor;
102✔
559
    }
560

561
    private static IMemberAccessor GetPropertyAccessor<TValue>(Expression<Func<TEntity, TValue>> property)
562
        => GetPropertyAccessor(_typeAccessor, property);
95✔
563
}
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