• 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

47.95
/src/FluentCommand/Query/UpdateBuilder.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 UPDATE statements with fluent, chainable methods.
8
/// </summary>
9
public class UpdateBuilder : UpdateBuilder<UpdateBuilder>
10
{
11
    /// <summary>
12
    /// Initializes a new instance of the <see cref="UpdateBuilder"/> 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 UpdateBuilder(
18
        IQueryGenerator queryGenerator,
19
        List<QueryParameter> parameters,
20
        LogicalOperators logicalOperator = LogicalOperators.And)
21
        : base(queryGenerator, parameters, logicalOperator)
×
22
    {
23
    }
×
24
}
25

26
/// <summary>
27
/// Provides a generic base class for building SQL UPDATE statements with fluent, chainable methods.
28
/// </summary>
29
/// <typeparam name="TBuilder">The type of the builder for fluent chaining.</typeparam>
30
public abstract class UpdateBuilder<TBuilder> : WhereBuilder<TBuilder>
31
    where TBuilder : UpdateBuilder<TBuilder>
32
{
33
    /// <summary>
34
    /// Initializes a new instance of the <see cref="UpdateBuilder{TBuilder}"/> class.
35
    /// </summary>
36
    /// <param name="queryGenerator">The <see cref="IQueryGenerator"/> used to generate SQL expressions.</param>
37
    /// <param name="parameters">The list of <see cref="QueryParameter"/> objects for the query.</param>
38
    /// <param name="logicalOperator">The logical operator (<see cref="LogicalOperators"/>) to combine WHERE expressions. Defaults to <see cref="LogicalOperators.And"/>.</param>
39
    protected UpdateBuilder(
40
        IQueryGenerator queryGenerator,
41
        List<QueryParameter> parameters,
42
        LogicalOperators logicalOperator = LogicalOperators.And)
43
        : base(queryGenerator, parameters, logicalOperator)
6✔
44
    {
45
    }
6✔
46

47
    /// <summary>
48
    /// Gets the collection of update expressions for the UPDATE statement.
49
    /// </summary>
50
    /// <value>
51
    /// A <see cref="HashSet{UpdateExpression}"/> containing the update expressions.
52
    /// </value>
53
    protected HashSet<UpdateExpression> UpdateExpressions { get; } = new();
23✔
54

55
    /// <summary>
56
    /// Gets the collection of output column expressions for the UPDATE statement.
57
    /// </summary>
58
    /// <value>
59
    /// A <see cref="HashSet{ColumnExpression}"/> containing the output column expressions.
60
    /// </value>
61
    protected HashSet<ColumnExpression> OutputExpressions { get; } = new();
18✔
62

63
    /// <summary>
64
    /// Gets the collection of FROM table expressions for the UPDATE statement.
65
    /// </summary>
66
    /// <value>
67
    /// A <see cref="HashSet{TableExpression}"/> containing the FROM table expressions.
68
    /// </value>
69
    protected HashSet<TableExpression> FromExpressions { get; } = new();
13✔
70

71
    /// <summary>
72
    /// Gets the collection of JOIN expressions for the UPDATE statement.
73
    /// </summary>
74
    /// <value>
75
    /// A <see cref="HashSet{JoinExpression}"/> containing the JOIN expressions.
76
    /// </value>
77
    protected HashSet<JoinExpression> JoinExpressions { get; } = new();
13✔
78

79
    /// <summary>
80
    /// Gets the target table expression for the UPDATE statement.
81
    /// </summary>
82
    /// <value>
83
    /// The <see cref="TableExpression"/> representing the target table.
84
    /// </value>
85
    protected TableExpression? TableExpression { get; private set; }
24✔
86

87
    /// <summary>
88
    /// Sets the target table to update.
89
    /// </summary>
90
    /// <param name="tableName">The name of the table.</param>
91
    /// <param name="tableSchema">The schema of the table (optional).</param>
92
    /// <param name="tableAlias">The alias for the table (optional).</param>
93
    /// <returns>
94
    /// The same builder instance for method chaining.
95
    /// </returns>
96
    public TBuilder Table(
97
        string tableName,
98
        string? tableSchema = null,
99
        string? tableAlias = null)
100
    {
101
        TableExpression = new TableExpression(tableName, tableSchema, tableAlias);
6✔
102

103
        return (TBuilder)this;
6✔
104
    }
105

106
    /// <summary>
107
    /// Adds a value for the specified column name and value.
108
    /// </summary>
109
    /// <typeparam name="TValue">The type of the value.</typeparam>
110
    /// <param name="columnName">The name of the column to update.</param>
111
    /// <param name="parameterValue">The value to set for the column.</param>
112
    /// <returns>
113
    /// The same builder instance for method chaining.
114
    /// </returns>
115
    public TBuilder Value<TValue>(
116
        string columnName,
117
        TValue? parameterValue)
118
    {
119
        return Value(columnName, parameterValue, typeof(TValue));
11✔
120
    }
121

122
    /// <summary>
123
    /// Adds a value for the specified column name, value, and type.
124
    /// </summary>
125
    /// <param name="columnName">The name of the column to update.</param>
126
    /// <param name="parameterValue">The value to set for the column.</param>
127
    /// <param name="parameterType">The type of the parameter value.</param>
128
    /// <returns>
129
    /// The same builder instance for method chaining.
130
    /// </returns>
131
    /// <exception cref="ArgumentException">Thrown if <paramref name="columnName"/> is null or empty.</exception>
132
    public TBuilder Value(
133
        string columnName,
134
        object? parameterValue,
135
        Type parameterType)
136
    {
137
        if (string.IsNullOrWhiteSpace(columnName))
11!
138
            throw new ArgumentException($"'{nameof(columnName)}' cannot be null or empty.", nameof(columnName));
×
139

140
        var paramterName = NextParameter();
11✔
141
        var updateClause = new UpdateExpression(columnName, paramterName);
11✔
142

143
        UpdateExpressions.Add(updateClause);
11✔
144
        Parameters.Add(new QueryParameter(paramterName, parameterValue, parameterType));
11✔
145

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

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

167
        return Value(columnName, parameterValue);
×
168
    }
169

170
    /// <summary>
171
    /// Adds an OUTPUT clause for the specified column names.
172
    /// </summary>
173
    /// <param name="columnNames">The collection of column names to include in the OUTPUT clause.</param>
174
    /// <param name="tableAlias">The alias for the table (optional).</param>
175
    /// <returns>
176
    /// The same builder instance for method chaining.
177
    /// </returns>
178
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="columnNames"/> is <c>null</c>.</exception>
179
    public TBuilder Output(
180
        IEnumerable<string> columnNames,
181
        string? tableAlias = null)
182
    {
183
        if (columnNames is null)
×
184
            throw new ArgumentNullException(nameof(columnNames));
×
185

186
        foreach (var column in columnNames)
×
187
            Output(column, tableAlias);
×
188

189
        return (TBuilder)this;
×
190
    }
191

192
    /// <summary>
193
    /// Adds an OUTPUT clause for the specified column name.
194
    /// </summary>
195
    /// <param name="columnName">The name of the column to include in the OUTPUT clause.</param>
196
    /// <param name="tableAlias">The alias for the table (optional).</param>
197
    /// <param name="columnAlias">The alias for the output column (optional).</param>
198
    /// <returns>
199
    /// The same builder instance for method chaining.
200
    /// </returns>
201
    public TBuilder Output(
202
        string columnName,
203
        string? tableAlias = null,
204
        string? columnAlias = null)
205
    {
206
        var outputClause = new ColumnExpression(columnName, tableAlias, columnAlias);
6✔
207

208
        OutputExpressions.Add(outputClause);
6✔
209

210
        return (TBuilder)this;
6✔
211
    }
212

213
    /// <summary>
214
    /// Conditionally adds an OUTPUT clause for the specified column name if the condition is met.
215
    /// </summary>
216
    /// <param name="columnName">The name of the column to include in the OUTPUT clause.</param>
217
    /// <param name="tableAlias">The alias for the table (optional).</param>
218
    /// <param name="columnAlias">The alias for the output column (optional).</param>
219
    /// <param name="condition">A function that determines whether to add the OUTPUT clause. If <c>null</c>, the clause is always added.</param>
220
    /// <returns>
221
    /// The same builder instance for method chaining.
222
    /// </returns>
223
    public TBuilder OutputIf(
224
        string columnName,
225
        string? tableAlias = null,
226
        string? columnAlias = null,
227
        Func<string, bool>? condition = null)
228
    {
229
        if (condition != null && !condition(columnName))
×
230
            return (TBuilder)this;
×
231

232
        return Output(columnName, tableAlias, columnAlias);
×
233
    }
234

235
    /// <summary>
236
    /// Adds a FROM clause to the UPDATE statement.
237
    /// </summary>
238
    /// <param name="tableName">The name of the table to include in the FROM clause.</param>
239
    /// <param name="tableSchema">The schema of the table (optional).</param>
240
    /// <param name="tableAlias">The alias for the table (optional).</param>
241
    /// <returns>
242
    /// The same builder instance for method chaining.
243
    /// </returns>
244
    public virtual TBuilder From(
245
        string tableName,
246
        string? tableSchema = null,
247
        string? tableAlias = null)
248
    {
249
        var fromClause = new TableExpression(tableName, tableSchema, tableAlias);
1✔
250

251
        FromExpressions.Add(fromClause);
1✔
252

253
        return (TBuilder)this;
1✔
254
    }
255

256
    /// <summary>
257
    /// Adds a raw FROM clause to the UPDATE statement.
258
    /// </summary>
259
    /// <param name="fromClause">The raw SQL FROM clause.</param>
260
    /// <returns>
261
    /// The same builder instance for method chaining.
262
    /// </returns>
263
    public TBuilder FromRaw(string fromClause)
264
    {
265
        if (fromClause.HasValue())
×
266
            FromExpressions.Add(new TableExpression(fromClause, IsRaw: true));
×
267

268
        return (TBuilder)this;
×
269
    }
270

271
    /// <summary>
272
    /// Adds a JOIN clause to the UPDATE statement using the specified builder action.
273
    /// </summary>
274
    /// <param name="builder">An action that configures the join using a <see cref="JoinBuilder"/>.</param>
275
    /// <returns>
276
    /// The same builder instance for method chaining.
277
    /// </returns>
278
    public TBuilder Join(Action<JoinBuilder> builder)
279
    {
280
        var innerBuilder = new JoinBuilder(QueryGenerator, Parameters);
×
281
        builder(innerBuilder);
×
282

283
        JoinExpressions.Add(innerBuilder.BuildExpression());
×
284

285
        return (TBuilder)this;
×
286
    }
287

288
    /// <summary>
289
    /// Builds the SQL UPDATE statement using the current configuration.
290
    /// </summary>
291
    /// <returns>
292
    /// A <see cref="QueryStatement"/> containing the SQL UPDATE statement and its parameters.
293
    /// </returns>
294
    public override QueryStatement? BuildStatement()
295
    {
296
        if (TableExpression is null)
6!
NEW
297
            throw new InvalidOperationException("Table must be specified before building an update statement.");
×
298

299
        var updateStatement = new UpdateStatement(
6✔
300
            TableExpression,
6✔
301
            UpdateExpressions,
6✔
302
            OutputExpressions,
6✔
303
            FromExpressions,
6✔
304
            JoinExpressions,
6✔
305
            WhereExpressions,
6✔
306
            CommentExpressions);
6✔
307

308
        var statement = QueryGenerator.BuildUpdate(updateStatement);
6✔
309

310
        return new QueryStatement(statement, Parameters);
6✔
311
    }
312
}
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