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

loresoft / FluentCommand / 26594986582

28 May 2026 06:28PM UTC coverage: 55.553%. First build
26594986582

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%)

4389 of 7130 relevant lines covered (61.56%)

312.88 hits per line

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

50.0
/src/FluentCommand/Query/UpdateBuilder.cs
1
using System.Text.Json;
2
using System.Text.Json.Serialization.Metadata;
3

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

7
namespace FluentCommand.Query;
8

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

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

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

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

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

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

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

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

106
        return (TBuilder)this;
10✔
107
    }
108

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

125
    /// <summary>
126
    /// Adds a value for the specified column name, value, and type.
127
    /// </summary>
128
    /// <param name="columnName">The name of the column to update.</param>
129
    /// <param name="parameterValue">The value to set for the column.</param>
130
    /// <param name="parameterType">The type of the parameter value.</param>
131
    /// <returns>
132
    /// The same builder instance for method chaining.
133
    /// </returns>
134
    /// <exception cref="ArgumentException">Thrown if <paramref name="columnName"/> is null or empty.</exception>
135
    public TBuilder Value(
136
        string columnName,
137
        object? parameterValue,
138
        Type parameterType)
139
    {
140
        ArgumentException.ThrowIfNullOrWhiteSpace(columnName);
15✔
141
        ArgumentNullException.ThrowIfNull(parameterType);
15✔
142

143
        var paramterName = NextParameter();
15✔
144
        var updateClause = new UpdateExpression(columnName, paramterName);
15✔
145

146
        UpdateExpressions.Add(updateClause);
15✔
147
        Parameters.Add(new QueryParameter(paramterName, parameterValue, parameterType));
15✔
148

149
        return (TBuilder)this;
15✔
150
    }
151

152
    /// <summary>
153
    /// Conditionally adds a value for the specified column name and value if the condition is met.
154
    /// </summary>
155
    /// <typeparam name="TValue">The type of the value.</typeparam>
156
    /// <param name="columnName">The name of the column to update.</param>
157
    /// <param name="parameterValue">The value to set for the column.</param>
158
    /// <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>
159
    /// <returns>
160
    /// The same builder instance for method chaining.
161
    /// </returns>
162
    public TBuilder ValueIf<TValue>(
163
        string columnName,
164
        TValue? parameterValue,
165
        Func<string, TValue?, bool> condition)
166
    {
167
        if (condition != null && !condition(columnName, parameterValue))
×
168
            return (TBuilder)this;
×
169

170
        return Value(columnName, parameterValue);
×
171
    }
172

173
    /// <summary>
174
    /// Adds a value for the specified column name with the value serialized as JSON using the specified <paramref name="options" />.
175
    /// </summary>
176
    /// <typeparam name="TValue">The type of the value.</typeparam>
177
    /// <param name="columnName">The name of the column to update.</param>
178
    /// <param name="parameterValue">The value to serialize as JSON and set for the column.</param>
179
    /// <param name="options">The <see cref="JsonSerializerOptions"/> to use when serializing.</param>
180
    /// <returns>
181
    /// The same builder instance for method chaining.
182
    /// </returns>
183
    public TBuilder ValueJson<TValue>(
184
        string columnName,
185
        TValue? parameterValue,
186
        JsonSerializerOptions? options = null)
187
    {
NEW
188
        var json = parameterValue is not null
×
NEW
189
            ? JsonSerializer.Serialize(parameterValue, options)
×
NEW
190
            : null;
×
191

NEW
192
        return Value(columnName, json);
×
193
    }
194

195
    /// <summary>
196
    /// Adds a value for the specified column name with the value serialized as JSON using the specified <paramref name="jsonTypeInfo" />.
197
    /// </summary>
198
    /// <typeparam name="TValue">The type of the value.</typeparam>
199
    /// <param name="columnName">The name of the column to update.</param>
200
    /// <param name="parameterValue">The value to serialize as JSON and set for the column.</param>
201
    /// <param name="jsonTypeInfo">The <see cref="JsonTypeInfo{T}"/> to use when serializing.</param>
202
    /// <returns>
203
    /// The same builder instance for method chaining.
204
    /// </returns>
205
    public TBuilder ValueJson<TValue>(
206
        string columnName,
207
        TValue? parameterValue,
208
        JsonTypeInfo<TValue> jsonTypeInfo)
209
    {
210
        ArgumentNullException.ThrowIfNull(jsonTypeInfo);
4✔
211

212
        var json = parameterValue is not null
4!
213
            ? JsonSerializer.Serialize(parameterValue, jsonTypeInfo)
4✔
214
            : null;
4✔
215

216
        return Value(columnName, json);
4✔
217
    }
218

219
    /// <summary>
220
    /// Adds an OUTPUT clause for the specified column names.
221
    /// </summary>
222
    /// <param name="columnNames">The collection of column names to include in the OUTPUT clause.</param>
223
    /// <param name="tableAlias">The alias for the table (optional).</param>
224
    /// <returns>
225
    /// The same builder instance for method chaining.
226
    /// </returns>
227
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="columnNames"/> is <c>null</c>.</exception>
228
    public TBuilder Output(
229
        IEnumerable<string> columnNames,
230
        string? tableAlias = null)
231
    {
NEW
232
        ArgumentNullException.ThrowIfNull(columnNames);
×
233

234
        foreach (var column in columnNames)
×
235
            Output(column, tableAlias);
×
236

237
        return (TBuilder)this;
×
238
    }
239

240
    /// <summary>
241
    /// Adds an OUTPUT clause for the specified column name.
242
    /// </summary>
243
    /// <param name="columnName">The name of the column to include in the OUTPUT clause.</param>
244
    /// <param name="tableAlias">The alias for the table (optional).</param>
245
    /// <param name="columnAlias">The alias for the output column (optional).</param>
246
    /// <returns>
247
    /// The same builder instance for method chaining.
248
    /// </returns>
249
    public TBuilder Output(
250
        string columnName,
251
        string? tableAlias = null,
252
        string? columnAlias = null)
253
    {
254
        var outputClause = new ColumnExpression(columnName, tableAlias, columnAlias);
6✔
255

256
        OutputExpressions.Add(outputClause);
6✔
257

258
        return (TBuilder)this;
6✔
259
    }
260

261
    /// <summary>
262
    /// Conditionally adds an OUTPUT clause for the specified column name if the condition is met.
263
    /// </summary>
264
    /// <param name="columnName">The name of the column to include in the OUTPUT clause.</param>
265
    /// <param name="tableAlias">The alias for the table (optional).</param>
266
    /// <param name="columnAlias">The alias for the output column (optional).</param>
267
    /// <param name="condition">A function that determines whether to add the OUTPUT 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 OutputIf(
272
        string columnName,
273
        string? tableAlias = null,
274
        string? columnAlias = null,
275
        Func<string, bool>? condition = null)
276
    {
277
        if (condition != null && !condition(columnName))
×
278
            return (TBuilder)this;
×
279

280
        return Output(columnName, tableAlias, columnAlias);
×
281
    }
282

283
    /// <summary>
284
    /// Adds a FROM clause to the UPDATE statement.
285
    /// </summary>
286
    /// <param name="tableName">The name of the table to include in the FROM clause.</param>
287
    /// <param name="tableSchema">The schema of the table (optional).</param>
288
    /// <param name="tableAlias">The alias for the table (optional).</param>
289
    /// <returns>
290
    /// The same builder instance for method chaining.
291
    /// </returns>
292
    public virtual TBuilder From(
293
        string tableName,
294
        string? tableSchema = null,
295
        string? tableAlias = null)
296
    {
297
        var fromClause = new TableExpression(tableName, tableSchema, tableAlias);
1✔
298

299
        FromExpressions.Add(fromClause);
1✔
300

301
        return (TBuilder)this;
1✔
302
    }
303

304
    /// <summary>
305
    /// Adds a raw FROM clause to the UPDATE statement.
306
    /// </summary>
307
    /// <param name="fromClause">The raw SQL FROM clause.</param>
308
    /// <returns>
309
    /// The same builder instance for method chaining.
310
    /// </returns>
311
    public TBuilder FromRaw(string fromClause)
312
    {
313
        if (fromClause.HasValue())
×
314
            FromExpressions.Add(new TableExpression(fromClause, IsRaw: true));
×
315

316
        return (TBuilder)this;
×
317
    }
318

319
    /// <summary>
320
    /// Adds a JOIN clause to the UPDATE statement using the specified builder action.
321
    /// </summary>
322
    /// <param name="builder">An action that configures the join using a <see cref="JoinBuilder"/>.</param>
323
    /// <returns>
324
    /// The same builder instance for method chaining.
325
    /// </returns>
326
    public TBuilder Join(Action<JoinBuilder> builder)
327
    {
328
        var innerBuilder = new JoinBuilder(QueryGenerator, Parameters);
×
329
        builder(innerBuilder);
×
330

331
        JoinExpressions.Add(innerBuilder.BuildExpression());
×
332

333
        return (TBuilder)this;
×
334
    }
335

336
    /// <summary>
337
    /// Builds the SQL UPDATE statement using the current configuration.
338
    /// </summary>
339
    /// <returns>
340
    /// A <see cref="QueryStatement"/> containing the SQL UPDATE statement and its parameters.
341
    /// </returns>
342
    public override QueryStatement? BuildStatement()
343
    {
344
        if (TableExpression is null)
10!
345
            throw new InvalidOperationException("Table must be specified before building an update statement.");
×
346

347
        var updateStatement = new UpdateStatement(
10✔
348
            TableExpression,
10✔
349
            UpdateExpressions,
10✔
350
            OutputExpressions,
10✔
351
            FromExpressions,
10✔
352
            JoinExpressions,
10✔
353
            WhereExpressions,
10✔
354
            CommentExpressions);
10✔
355

356
        var statement = QueryGenerator.BuildUpdate(updateStatement);
10✔
357

358
        return new QueryStatement(statement, Parameters);
10✔
359
    }
360
}
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