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

loresoft / FluentCommand / 23256000174

18 Mar 2026 04:13PM UTC coverage: 56.658% (+0.2%) from 56.486%
23256000174

push

github

pwelter34
Add SQL Server structured parameter support

1303 of 2875 branches covered (45.32%)

Branch coverage included in aggregate %.

106 of 149 new or added lines in 8 files covered. (71.14%)

6 existing lines in 1 file now uncovered.

3969 of 6430 relevant lines covered (61.73%)

361.39 hits per line

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

30.77
/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 = _typeAccessor.FindProperty(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
    {
65
        var propertyAccessor = _typeAccessor.FindProperty(property);
×
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();
×
NEW
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 = _typeAccessor.FindProperty(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
    {
140
        var propertyAccessor = _typeAccessor.FindProperty(property);
×
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 = _typeAccessor.FindProperty(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

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

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

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

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

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

280
        return WhereIn(propertyAccessor?.Column, parameterValues, tableAlias);
×
281
    }
282

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

291
        return WhereInIf(propertyAccessor?.Column, parameterValues, condition);
×
292
    }
293

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

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

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

311
        builder(innerBuilder);
×
312

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

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

318
        return this;
×
319
    }
320

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

326
        builder(innerBuilder);
×
327

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

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

333
        return 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
        // add table and schema from attribute if not set
345
        if (TableExpression == null)
6✔
346
            Table(_typeAccessor.TableName, _typeAccessor.TableSchema);
6✔
347

348
        return base.BuildStatement();
6✔
349
    }
350
}
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