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

loresoft / FluentCommand / 23278216331

19 Mar 2026 03:19AM UTC coverage: 57.398% (+0.7%) from 56.658%
23278216331

push

github

pwelter34
Enable nullable and improve source generators

1403 of 3069 branches covered (45.72%)

Branch coverage included in aggregate %.

527 of 907 new or added lines in 58 files covered. (58.1%)

22 existing lines in 10 files now uncovered.

4288 of 6846 relevant lines covered (62.64%)

330.58 hits per line

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

28.89
/src/FluentCommand/Query/WhereBuilder.cs
1
using FluentCommand.Extensions;
2
using FluentCommand.Query.Generators;
3

4
namespace FluentCommand.Query;
5

6
/// <summary>
7
/// Provides a builder for constructing SQL WHERE clauses with fluent, chainable methods.
8
/// </summary>
9
public class WhereBuilder : WhereBuilder<WhereBuilder>
10
{
11
    /// <summary>
12
    /// Initializes a new instance of the <see cref="WhereBuilder"/> class.
13
    /// </summary>
14
    /// <param name="queryGenerator">The <see cref="IQueryGenerator"/> used to generate SQL expressions.</param>
15
    /// <param name="parameters">The list of <see cref="QueryParameter"/> objects for the query.</param>
16
    /// <param name="logicalOperator">The logical operator (<see cref="LogicalOperators"/>) to combine WHERE expressions. Defaults to <see cref="LogicalOperators.And"/>.</param>
17
    public WhereBuilder(
18
        IQueryGenerator queryGenerator,
19
        List<QueryParameter> parameters,
20
        LogicalOperators logicalOperator = LogicalOperators.And)
21
        : base(queryGenerator, parameters, logicalOperator)
×
22
    {
23
    }
×
24

25
    /// <inheritdoc />
26
    public override QueryStatement? BuildStatement()
27
    {
28
        var statement = QueryGenerator.BuildWhere(
×
29
            whereExpressions: WhereExpressions
×
30
        );
×
31

NEW
32
        if (statement.IsNullOrWhiteSpace())
×
NEW
33
            return null;
×
34

UNCOV
35
        return new QueryStatement(statement, Parameters);
×
36
    }
37
}
38

39
/// <summary>
40
/// Provides a generic base class for building SQL WHERE clauses with fluent, chainable methods.
41
/// </summary>
42
/// <typeparam name="TBuilder">The type of the builder for fluent chaining.</typeparam>
43
public abstract class WhereBuilder<TBuilder> : StatementBuilder<TBuilder>
44
    where TBuilder : WhereBuilder<TBuilder>
45
{
46
    /// <summary>
47
    /// Initializes a new instance of the <see cref="WhereBuilder{TBuilder}"/> class.
48
    /// </summary>
49
    /// <param name="queryGenerator">The <see cref="IQueryGenerator"/> used to generate SQL expressions.</param>
50
    /// <param name="parameters">The list of <see cref="QueryParameter"/> objects for the query.</param>
51
    /// <param name="logicalOperator">The logical operator (<see cref="LogicalOperators"/>) to combine WHERE expressions. Defaults to <see cref="LogicalOperators.And"/>.</param>
52
    protected WhereBuilder(IQueryGenerator queryGenerator, List<QueryParameter> parameters, LogicalOperators logicalOperator = LogicalOperators.And)
53
        : base(queryGenerator, parameters)
70✔
54
    {
55
        LogicalOperator = logicalOperator;
70✔
56
    }
70✔
57

58
    /// <summary>
59
    /// Gets the collection of WHERE expressions for the query.
60
    /// </summary>
61
    /// <value>
62
    /// A <see cref="HashSet{WhereExpression}"/> containing the WHERE expressions.
63
    /// </value>
64
    protected HashSet<WhereExpression> WhereExpressions { get; } = new();
212✔
65

66
    /// <summary>
67
    /// Gets the logical operator used to combine WHERE expressions.
68
    /// </summary>
69
    /// <value>
70
    /// The <see cref="LogicalOperators"/> value.
71
    /// </value>
72
    protected LogicalOperators LogicalOperator { get; }
7✔
73

74
    /// <summary>
75
    /// Adds a WHERE clause for the specified column, value, and operator.
76
    /// </summary>
77
    /// <typeparam name="TValue">The type of the value.</typeparam>
78
    /// <param name="columnName">The name of the column.</param>
79
    /// <param name="parameterValue">The value to compare.</param>
80
    /// <param name="filterOperator">The filter operator (<see cref="FilterOperators"/>). Defaults to <see cref="FilterOperators.Equal"/>.</param>
81
    /// <returns>
82
    /// The same builder instance for method chaining.
83
    /// </returns>
84
    public TBuilder Where<TValue>(
85
       string columnName,
86
       TValue parameterValue,
87
       FilterOperators filterOperator = FilterOperators.Equal)
88
    {
89
        return Where(columnName, parameterValue, null, filterOperator);
2✔
90
    }
91

92
    /// <summary>
93
    /// Adds a WHERE clause for the specified column, value, operator, and table alias.
94
    /// </summary>
95
    /// <typeparam name="TValue">The type of the value.</typeparam>
96
    /// <param name="columnName">The name of the column.</param>
97
    /// <param name="parameterValue">The value to compare.</param>
98
    /// <param name="tableAlias">The table alias, or <c>null</c> if not applicable.</param>
99
    /// <param name="filterOperator">The filter operator (<see cref="FilterOperators"/>). Defaults to <see cref="FilterOperators.Equal"/>.</param>
100
    /// <returns>
101
    /// The same builder instance for method chaining.
102
    /// </returns>
103
    public TBuilder Where<TValue>(
104
        string columnName,
105
        TValue? parameterValue,
106
        string? tableAlias,
107
        FilterOperators filterOperator = FilterOperators.Equal)
108
    {
109
        var parameterName = NextParameter();
49✔
110

111
        WhereExpressions.Add(new WhereExpression(columnName, parameterName, tableAlias, filterOperator));
49✔
112
        Parameters.Add(new QueryParameter(parameterName, parameterValue, typeof(TValue)));
49✔
113

114
        return (TBuilder)this;
49✔
115
    }
116

117
    /// <summary>
118
    /// Adds a WHERE IN clause for the specified column, values, and optional table alias.
119
    /// </summary>
120
    /// <typeparam name="TValue">The type of the values.</typeparam>
121
    /// <param name="columnName">The name of the column.</param>
122
    /// <param name="parameterValues">The collection of values for the IN clause.</param>
123
    /// <param name="tableAlias">The table alias, or <c>null</c> if not applicable.</param>
124
    /// <returns>
125
    /// The same builder instance for method chaining.
126
    /// </returns>
127
    public TBuilder WhereIn<TValue>(
128
        string columnName,
129
        IEnumerable<TValue> parameterValues,
130
        string? tableAlias = null)
131
    {
132
        var parameterNames = new List<string>();
9✔
133
        foreach (var parameterValue in parameterValues)
64✔
134
        {
135
            var parameterName = NextParameter();
23✔
136
            var parameter = new QueryParameter(parameterName, parameterValue, typeof(TValue));
23✔
137

138
            Parameters.Add(parameter);
23✔
139
            parameterNames.Add(parameterName);
23✔
140
        }
141

142
        var whereParameter = parameterNames.ToDelimitedString();
9✔
143

144
        WhereExpressions.Add(new WhereExpression(columnName, whereParameter, tableAlias, FilterOperators.In));
9✔
145

146
        return (TBuilder)this;
9✔
147
    }
148

149
    /// <summary>
150
    /// Conditionally adds a WHERE IN clause for the specified column and values if the condition is met.
151
    /// </summary>
152
    /// <typeparam name="TValue">The type of the values.</typeparam>
153
    /// <param name="columnName">The name of the column.</param>
154
    /// <param name="parameterValues">The collection of values for the IN clause.</param>
155
    /// <param name="condition">A function that determines whether to add the clause. If <c>null</c>, the clause is always added.</param>
156
    /// <returns>
157
    /// The same builder instance for method chaining.
158
    /// </returns>
159
    public TBuilder WhereInIf<TValue>(
160
        string columnName,
161
        IEnumerable<TValue> parameterValues,
162
        Func<string, IEnumerable<TValue>, bool>? condition = null)
163
    {
164
        if (condition != null && !condition(columnName, parameterValues))
1!
165
            return (TBuilder)this;
×
166

167
        return WhereIn(columnName, parameterValues);
1✔
168
    }
169

170
    /// <summary>
171
    /// Conditionally adds a WHERE IN clause for the specified column, values, and table alias if the condition is met.
172
    /// </summary>
173
    /// <typeparam name="TValue">The type of the values.</typeparam>
174
    /// <param name="columnName">The name of the column.</param>
175
    /// <param name="parameterValues">The collection of values for the IN clause.</param>
176
    /// <param name="tableAlias">The table alias, or <c>null</c> if not applicable.</param>
177
    /// <param name="condition">A function that determines whether to add the clause. If <c>null</c>, the clause is always added.</param>
178
    /// <returns>
179
    /// The same builder instance for method chaining.
180
    /// </returns>
181
    public TBuilder WhereInIf<TValue>(
182
        string columnName,
183
        IEnumerable<TValue> parameterValues,
184
        string? tableAlias,
185
        Func<string, IEnumerable<TValue>, bool>? condition = null)
186
    {
187
        if (condition != null && !condition(columnName, parameterValues))
×
188
            return (TBuilder)this;
×
189

190
        return WhereIn(columnName, parameterValues, tableAlias);
×
191
    }
192

193
    /// <summary>
194
    /// Conditionally adds a WHERE clause for the specified column, value, and operator if the condition is met.
195
    /// </summary>
196
    /// <typeparam name="TValue">The type of the value.</typeparam>
197
    /// <param name="columnName">The name of the column.</param>
198
    /// <param name="parameterValue">The value to compare.</param>
199
    /// <param name="filterOperator">The filter operator (<see cref="FilterOperators"/>). Defaults to <see cref="FilterOperators.Equal"/>.</param>
200
    /// <param name="condition">A function that determines whether to add the clause. If <c>null</c>, the clause is always added.</param>
201
    /// <returns>
202
    /// The same builder instance for method chaining.
203
    /// </returns>
204
    public TBuilder WhereIf<TValue>(
205
        string columnName,
206
        TValue? parameterValue,
207
        FilterOperators filterOperator = FilterOperators.Equal,
208
        Func<string, TValue?, bool>? condition = null)
209
    {
210
        return WhereIf(columnName, parameterValue, null, filterOperator, condition);
×
211
    }
212

213
    /// <summary>
214
    /// Conditionally adds a WHERE clause for the specified column, value, operator, and table alias if the condition is met.
215
    /// </summary>
216
    /// <typeparam name="TValue">The type of the value.</typeparam>
217
    /// <param name="columnName">The name of the column.</param>
218
    /// <param name="parameterValue">The value to compare.</param>
219
    /// <param name="tableAlias">The table alias, or <c>null</c> if not applicable.</param>
220
    /// <param name="filterOperator">The filter operator (<see cref="FilterOperators"/>). Defaults to <see cref="FilterOperators.Equal"/>.</param>
221
    /// <param name="condition">A function that determines whether to add the clause. If <c>null</c>, the clause is always added.</param>
222
    /// <returns>
223
    /// The same builder instance for method chaining.
224
    /// </returns>
225
    public TBuilder WhereIf<TValue>(
226
        string columnName,
227
        TValue? parameterValue,
228
        string? tableAlias,
229
        FilterOperators filterOperator = FilterOperators.Equal,
230
        Func<string, TValue?, bool>? condition = null)
231
    {
232
        if (condition != null && !condition(columnName, parameterValue))
×
233
            return (TBuilder)this;
×
234

235
        return Where(columnName, parameterValue, tableAlias, filterOperator);
×
236
    }
237

238
    /// <summary>
239
    /// Adds a raw WHERE clause to the query.
240
    /// </summary>
241
    /// <param name="whereClause">The raw SQL WHERE clause.</param>
242
    /// <param name="parameters">The collection of <see cref="QueryParameter"/> objects for the clause, or <c>null</c> if none.</param>
243
    /// <returns>
244
    /// The same builder instance for method chaining.
245
    /// </returns>
246
    /// <exception cref="ArgumentException">Thrown if <paramref name="whereClause"/> is null or empty.</exception>
247
    public TBuilder WhereRaw(
248
        string whereClause,
249
        IEnumerable<QueryParameter>? parameters = null)
250
    {
251
        if (string.IsNullOrWhiteSpace(whereClause))
×
252
            throw new ArgumentException($"'{nameof(whereClause)}' cannot be null or empty.", nameof(whereClause));
×
253

254
        WhereExpressions.Add(new WhereExpression(whereClause, IsRaw: true));
×
255

256
        if (parameters != null)
×
257
            Parameters.AddRange(parameters);
×
258

259
        return (TBuilder)this;
×
260
    }
261

262
    /// <summary>
263
    /// Conditionally adds a raw WHERE clause to the query if the condition is met.
264
    /// </summary>
265
    /// <param name="whereClause">The raw SQL WHERE clause.</param>
266
    /// <param name="parameters">The collection of <see cref="QueryParameter"/> objects for the clause, or <c>null</c> if none.</param>
267
    /// <param name="condition">A function that determines whether to add the clause. If <c>null</c>, the clause is always added.</param>
268
    /// <returns>
269
    /// The same builder instance for method chaining.
270
    /// </returns>
271
    public TBuilder WhereRawIf(
272
        string whereClause,
273
        IEnumerable<QueryParameter>? parameters = null,
274
        Func<string, IEnumerable<QueryParameter>?, bool>? condition = null)
275
    {
276
        if (condition != null && !condition(whereClause, parameters))
×
277
            return (TBuilder)this;
×
278

279
        return WhereRaw(whereClause, parameters);
×
280
    }
281

282
    /// <summary>
283
    /// Adds a logical OR group of WHERE clauses using the specified builder action.
284
    /// </summary>
285
    /// <param name="builder">An action that configures the logical OR group using a <see cref="LogicalBuilder"/>.</param>
286
    /// <returns>
287
    /// The same builder instance for method chaining.
288
    /// </returns>
289
    public TBuilder WhereOr(Action<LogicalBuilder> builder)
290
    {
291
        var innerBuilder = new LogicalBuilder(QueryGenerator, Parameters, LogicalOperators.Or);
×
292
        builder(innerBuilder);
×
293

294
        var statement = innerBuilder.BuildStatement();
×
295

296
        if (statement != null && statement.Statement.HasValue())
×
297
            WhereExpressions.Add(new WhereExpression(statement.Statement, IsRaw: true));
×
298

299
        return (TBuilder)this;
×
300
    }
301

302
    /// <summary>
303
    /// Adds a logical AND group of WHERE clauses using the specified builder action.
304
    /// </summary>
305
    /// <param name="builder">An action that configures the logical AND group using a <see cref="LogicalBuilder"/>.</param>
306
    /// <returns>
307
    /// The same builder instance for method chaining.
308
    /// </returns>
309
    public TBuilder WhereAnd(Action<LogicalBuilder> builder)
310
    {
311
        var innerBuilder = new LogicalBuilder(QueryGenerator, Parameters, LogicalOperators.And);
×
312

313
        builder(innerBuilder);
×
314

315
        var statement = innerBuilder.BuildStatement();
×
316

317
        if (statement != null && statement.Statement.HasValue())
×
318
            WhereExpressions.Add(new WhereExpression(statement.Statement, IsRaw: true));
×
319

320
        return (TBuilder)this;
×
321
    }
322
}
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