• 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

36.36
/src/FluentCommand/Query/UpdateEntityBuilder.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 UPDATE 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 UpdateEntityBuilder<TEntity>
14
    : UpdateBuilder<UpdateEntityBuilder<TEntity>>, IWhereEntityBuilder<TEntity, UpdateEntityBuilder<TEntity>>
15
    where TEntity : class
16
{
17
    private static readonly TypeAccessor _typeAccessor = TypeAccessor.GetAccessor<TEntity>();
5✔
18

19
    /// <summary>
20
    /// Initializes a new instance of the <see cref="UpdateEntityBuilder{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 UpdateEntityBuilder(
26
        IQueryGenerator queryGenerator,
27
        List<QueryParameter> parameters,
28
        LogicalOperators logicalOperator = LogicalOperators.And)
29
        : base(queryGenerator, parameters, logicalOperator)
6✔
30
    {
31
    }
6✔
32

33
    /// <summary>
34
    /// Adds a value for the specified entity property and value.
35
    /// </summary>
36
    /// <typeparam name="TValue">The type of the value.</typeparam>
37
    /// <param name="property">An expression selecting the property to update.</param>
38
    /// <param name="parameterValue">The value to set for the property.</param>
39
    /// <returns>
40
    /// The same builder instance for method chaining.
41
    /// </returns>
42
    public UpdateEntityBuilder<TEntity> Value<TValue>(
43
        Expression<Func<TEntity, TValue>> property,
44
        TValue parameterValue)
45
    {
46
        var propertyAccessor = GetPropertyAccessor(property);
11✔
47
        return Value(propertyAccessor.Column, parameterValue);
11✔
48
    }
49

50
    /// <summary>
51
    /// Conditionally adds a value for the specified entity property and value if the condition is met.
52
    /// </summary>
53
    /// <typeparam name="TValue">The type of the value.</typeparam>
54
    /// <param name="property">An expression selecting the property to update.</param>
55
    /// <param name="parameterValue">The value to set for the property.</param>
56
    /// <param name="condition">A function that determines whether to add the value, based on the property name and value. If <c>null</c>, the value is always added.</param>
57
    /// <returns>
58
    /// The same builder instance for method chaining.
59
    /// </returns>
60
    public UpdateEntityBuilder<TEntity> ValueIf<TValue>(
61
        Expression<Func<TEntity, TValue>> property,
62
        TValue? parameterValue,
63
        Func<string, TValue?, bool> condition)
64
    {
NEW
65
        var propertyAccessor = GetPropertyAccessor(property);
×
NEW
66
        return ValueIf(propertyAccessor.Column, parameterValue, condition);
×
67
    }
68

69
    /// <summary>
70
    /// Adds values from the specified entity. If column names are provided,
71
    /// only those that match an entity property name will be included.
72
    /// </summary>
73
    /// <param name="entity">The entity to update.</param>
74
    /// <param name="columnNames">The column names to include (optional).</param>
75
    /// <returns>
76
    /// The same builder instance for method chaining.
77
    /// </returns>
78
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="entity"/> is <c>null</c>.</exception>
79
    public UpdateEntityBuilder<TEntity> Values(
80
        TEntity entity,
81
        IEnumerable<string>? columnNames = null)
82
    {
83
        if (entity is null)
×
84
            throw new ArgumentNullException(nameof(entity));
×
85

86
        var properties = _typeAccessor.GetProperties();
×
87
        var columnSet = new HashSet<string>(columnNames ?? []);
×
88

89
        foreach (var property in properties)
×
90
        {
91
            if (columnSet.Count > 0 && !columnSet.Contains(property.Column))
×
92
                continue;
93

94
            if (property.IsNotMapped || property.IsDatabaseGenerated)
×
95
                continue;
96

97
            // include the type to prevent issues with null
98
            Value(property.Column, property.GetValue(entity), property.MemberType);
×
99
        }
100

101
        return this;
×
102
    }
103

104
    /// <summary>
105
    /// Adds an OUTPUT clause for the specified entity property.
106
    /// </summary>
107
    /// <typeparam name="TValue">The type of the property value.</typeparam>
108
    /// <param name="property">An expression selecting the property to output.</param>
109
    /// <param name="tableAlias">The alias for the table (optional).</param>
110
    /// <param name="columnAlias">The alias for the output column (optional).</param>
111
    /// <returns>
112
    /// The same builder instance for method chaining.
113
    /// </returns>
114
    public UpdateEntityBuilder<TEntity> Output<TValue>(
115
        Expression<Func<TEntity, TValue>> property,
116
        string? tableAlias = null,
117
        string? columnAlias = null)
118
    {
119
        var propertyAccessor = GetPropertyAccessor(property);
6✔
120
        return Output(propertyAccessor.Column, tableAlias, columnAlias);
6✔
121
    }
122

123
    /// <summary>
124
    /// Conditionally adds an OUTPUT clause for the specified entity property if the condition is met.
125
    /// </summary>
126
    /// <typeparam name="TValue">The type of the property value.</typeparam>
127
    /// <param name="property">An expression selecting the property to output.</param>
128
    /// <param name="tableAlias">The alias for the table (optional).</param>
129
    /// <param name="columnAlias">The alias for the output column (optional).</param>
130
    /// <param name="condition">A function that determines whether to add the OUTPUT clause. If <c>null</c>, the clause is always added.</param>
131
    /// <returns>
132
    /// The same builder instance for method chaining.
133
    /// </returns>
134
    public UpdateEntityBuilder<TEntity> OutputIf<TValue>(
135
        Expression<Func<TEntity, TValue>> property,
136
        string? tableAlias = null,
137
        string? columnAlias = null,
138
        Func<string, bool>? condition = null)
139
    {
NEW
140
        var propertyAccessor = GetPropertyAccessor(property);
×
NEW
141
        return OutputIf(propertyAccessor.Column, tableAlias, columnAlias, condition);
×
142
    }
143

144
    /// <summary>
145
    /// Sets the target table for the UPDATE statement using the entity's mapping information by default.
146
    /// </summary>
147
    /// <param name="tableName">The name of the table (optional, defaults to entity mapping).</param>
148
    /// <param name="tableSchema">The schema of the table (optional, defaults to entity mapping).</param>
149
    /// <param name="tableAlias">The alias for the table (optional).</param>
150
    /// <returns>
151
    /// The same builder instance for method chaining.
152
    /// </returns>
153
    public override UpdateEntityBuilder<TEntity> From(
154
        string? tableName = null,
155
        string? tableSchema = null,
156
        string? tableAlias = null)
157
    {
158
        return base.From(
1✔
159
            tableName ?? _typeAccessor.TableName,
1✔
160
            tableSchema ?? _typeAccessor.TableSchema,
1✔
161
            tableAlias);
1✔
162
    }
163

164
    /// <summary>
165
    /// Adds a JOIN clause to the UPDATE statement using the specified builder action for the right entity.
166
    /// </summary>
167
    /// <typeparam name="TRight">The type of the right join entity.</typeparam>
168
    /// <param name="builder">An action that configures the join using a <see cref="JoinEntityBuilder{TEntity, TRight}"/>.</param>
169
    /// <returns>
170
    /// The same builder instance for method chaining.
171
    /// </returns>
172
    public UpdateEntityBuilder<TEntity> Join<TRight>(Action<JoinEntityBuilder<TEntity, TRight>> builder)
173
        where TRight : class
174
    {
175
        var innerBuilder = new JoinEntityBuilder<TEntity, TRight>(QueryGenerator, Parameters);
1✔
176
        builder(innerBuilder);
1✔
177

178
        JoinExpressions.Add(innerBuilder.BuildExpression());
1✔
179

180
        return this;
1✔
181
    }
182

183
    /// <summary>
184
    /// Adds a JOIN clause to the UPDATE statement using the specified builder action for the left and right entities.
185
    /// </summary>
186
    /// <typeparam name="TLeft">The type of the left join entity.</typeparam>
187
    /// <typeparam name="TRight">The type of the right join entity.</typeparam>
188
    /// <param name="builder">An action that configures the join using a <see cref="JoinEntityBuilder{TLeft, TRight}"/>.</param>
189
    /// <returns>
190
    /// The same builder instance for method chaining.
191
    /// </returns>
192
    public UpdateEntityBuilder<TEntity> Join<TLeft, TRight>(Action<JoinEntityBuilder<TLeft, TRight>> builder)
193
        where TLeft : class
194
        where TRight : class
195
    {
196
        var innerBuilder = new JoinEntityBuilder<TLeft, TRight>(QueryGenerator, Parameters);
×
197
        builder(innerBuilder);
×
198

199
        JoinExpressions.Add(innerBuilder.BuildExpression());
×
200

201
        return this;
×
202
    }
203

204
    /// <inheritdoc />
205
    public UpdateEntityBuilder<TEntity> Where<TValue>(
206
        Expression<Func<TEntity, TValue>> property,
207
        TValue? parameterValue,
208
        FilterOperators filterOperator = FilterOperators.Equal)
209
    {
210
        return Where<TValue>(property, parameterValue, null, filterOperator);
5✔
211
    }
212

213
    /// <inheritdoc />
214
    public UpdateEntityBuilder<TEntity> Where<TValue>(
215
        Expression<Func<TEntity, TValue>> property,
216
        TValue? parameterValue,
217
        string? tableAlias,
218
        FilterOperators filterOperator = FilterOperators.Equal)
219
    {
220
        var propertyAccessor = GetPropertyAccessor(property);
5✔
221

222
        return Where(propertyAccessor.Column, parameterValue, tableAlias, filterOperator);
5✔
223
    }
224

225
    /// <summary>
226
    /// Adds a WHERE clause for the specified model property, value, filter operator, and table alias.
227
    /// </summary>
228
    /// <typeparam name="TModel">The type of the model.</typeparam>
229
    /// <typeparam name="TValue">The type of the value to compare.</typeparam>
230
    /// <param name="property">An expression selecting the property to filter on.</param>
231
    /// <param name="parameterValue">The value to compare the property against.</param>
232
    /// <param name="tableAlias">The alias of the table (optional).</param>
233
    /// <param name="filterOperator">The filter operator to use (default is <see cref="FilterOperators.Equal"/>).</param>
234
    /// <returns>
235
    /// The same builder instance for method chaining.
236
    /// </returns>
237
    public UpdateEntityBuilder<TEntity> Where<TModel, TValue>(
238
        Expression<Func<TModel, TValue>> property,
239
        TValue? parameterValue,
240
        string? tableAlias,
241
        FilterOperators filterOperator = FilterOperators.Equal)
242
    {
243
        var typeAccessor = TypeAccessor.GetAccessor<TModel>();
1✔
244
        var propertyAccessor = typeAccessor.FindProperty(property)
1!
245
            ?? throw new ArgumentException("The specified property does not exist on the entity.", nameof(property));
1✔
246

247
        return Where(propertyAccessor.Column, parameterValue, tableAlias, filterOperator);
1✔
248
    }
249

250
    /// <inheritdoc />
251
    public UpdateEntityBuilder<TEntity> WhereIf<TValue>(
252
        Expression<Func<TEntity, TValue>> property,
253
        TValue? parameterValue,
254
        FilterOperators filterOperator = FilterOperators.Equal,
255
        Func<string, TValue?, bool>? condition = null)
256
    {
257
        return WhereIf(property, parameterValue, null, filterOperator, condition);
×
258
    }
259

260
    /// <inheritdoc />
261
    public UpdateEntityBuilder<TEntity> WhereIf<TValue>(
262
        Expression<Func<TEntity, TValue>> property,
263
        TValue? parameterValue,
264
        string? tableAlias,
265
        FilterOperators filterOperator = FilterOperators.Equal,
266
        Func<string, TValue?, bool>? condition = null)
267
    {
NEW
268
        var propertyAccessor = GetPropertyAccessor(property);
×
269

270
        return WhereIf(propertyAccessor.Column, parameterValue, tableAlias, filterOperator, condition);
×
271
    }
272

273
    /// <inheritdoc />
274
    public UpdateEntityBuilder<TEntity> WhereIn<TValue>(
275
        Expression<Func<TEntity, TValue>> property,
276
        IEnumerable<TValue> parameterValues,
277
        string? tableAlias = null)
278
    {
NEW
279
        var propertyAccessor = GetPropertyAccessor(property);
×
280

NEW
281
        return WhereIn(propertyAccessor.Column, parameterValues, tableAlias);
×
282
    }
283

284
    /// <inheritdoc />
285
    public UpdateEntityBuilder<TEntity> WhereInIf<TValue>(
286
        Expression<Func<TEntity, TValue>> property,
287
        IEnumerable<TValue> parameterValues,
288
        Func<string, IEnumerable<TValue>, bool>? condition = null)
289
    {
NEW
290
        var propertyAccessor = GetPropertyAccessor(property);
×
291

NEW
292
        return WhereInIf(propertyAccessor.Column, parameterValues, condition);
×
293
    }
294

295
    /// <inheritdoc />
296
    public UpdateEntityBuilder<TEntity> WhereInIf<TValue>(
297
        Expression<Func<TEntity, TValue>> property,
298
        IEnumerable<TValue> parameterValues,
299
        string? tableAlias,
300
        Func<string, IEnumerable<TValue>, bool>? condition = null)
301
    {
NEW
302
        var propertyAccessor = GetPropertyAccessor(property);
×
303

NEW
304
        return WhereInIf(propertyAccessor.Column, parameterValues, tableAlias, condition);
×
305
    }
306

307
    /// <inheritdoc />
308
    public UpdateEntityBuilder<TEntity> WhereOr(Action<LogicalEntityBuilder<TEntity>> builder)
309
    {
310
        var innerBuilder = new LogicalEntityBuilder<TEntity>(QueryGenerator, Parameters, LogicalOperators.Or);
×
311

312
        builder(innerBuilder);
×
313

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

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

319
        return this;
×
320
    }
321

322
    /// <inheritdoc />
323
    public UpdateEntityBuilder<TEntity> WhereAnd(Action<LogicalEntityBuilder<TEntity>> builder)
324
    {
325
        var innerBuilder = new LogicalEntityBuilder<TEntity>(QueryGenerator, Parameters, LogicalOperators.And);
×
326

327
        builder(innerBuilder);
×
328

329
        var statement = innerBuilder.BuildStatement();
×
330

331
        if (statement != null && statement.Statement.HasValue())
×
332
            WhereExpressions.Add(new WhereExpression(statement.Statement, IsRaw: true));
×
333

334
        return this;
×
335
    }
336

337
    /// <summary>
338
    /// Builds the SQL UPDATE statement using the current configuration.
339
    /// </summary>
340
    /// <returns>
341
    /// A <see cref="QueryStatement"/> containing the SQL UPDATE statement and its parameters.
342
    /// </returns>
343
    public override QueryStatement? BuildStatement()
344
    {
345
        // add table and schema from attribute if not set
346
        if (TableExpression == null)
6✔
347
            Table(_typeAccessor.TableName, _typeAccessor.TableSchema);
6✔
348

349
        return base.BuildStatement();
6✔
350
    }
351

352
    private static IMemberAccessor GetPropertyAccessor<TValue>(Expression<Func<TEntity, TValue>> property)
353
    {
354
        var propertyAccessor = _typeAccessor.FindProperty(property);
22✔
355
        if (propertyAccessor is null)
22!
NEW
356
            throw new ArgumentException("The specified property does not exist on the entity.", nameof(property));
×
357

358
        return propertyAccessor;
22✔
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