• 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.3
/src/FluentCommand/Query/DeleteEntityBuilder.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 DELETE 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 DeleteEntityBuilder<TEntity>
14
    : DeleteBuilder<DeleteEntityBuilder<TEntity>>, IWhereEntityBuilder<TEntity, DeleteEntityBuilder<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="DeleteEntityBuilder{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 DeleteEntityBuilder(
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 an OUTPUT clause for the specified entity property.
35
    /// </summary>
36
    /// <typeparam name="TValue">The type of the property value.</typeparam>
37
    /// <param name="property">An expression selecting the property to output.</param>
38
    /// <param name="tableAlias">The alias for the table (optional).</param>
39
    /// <param name="columnAlias">The alias for the output column (optional).</param>
40
    /// <returns>
41
    /// The same builder instance for method chaining.
42
    /// </returns>
43
    public DeleteEntityBuilder<TEntity> Output<TValue>(
44
        Expression<Func<TEntity, TValue>> property,
45
        string? tableAlias = null,
46
        string? columnAlias = null)
47
    {
48
        var propertyAccessor = GetPropertyAccessor(property);
6✔
49
        return Output(propertyAccessor.Column, tableAlias, columnAlias);
6✔
50
    }
51

52
    /// <summary>
53
    /// Conditionally adds an OUTPUT clause for the specified entity property if the condition is met.
54
    /// </summary>
55
    /// <typeparam name="TValue">The type of the property value.</typeparam>
56
    /// <param name="property">An expression selecting the property to output.</param>
57
    /// <param name="tableAlias">The alias for the table (optional).</param>
58
    /// <param name="columnAlias">The alias for the output column (optional).</param>
59
    /// <param name="condition">A function that determines whether to add the OUTPUT clause. If <c>null</c>, the clause is always added.</param>
60
    /// <returns>
61
    /// The same builder instance for method chaining.
62
    /// </returns>
63
    public DeleteEntityBuilder<TEntity> OutputIf<TValue>(
64
        Expression<Func<TEntity, TValue>> property,
65
        string? tableAlias = null,
66
        string? columnAlias = null,
67
        Func<string, bool>? condition = null)
68
    {
NEW
69
        var propertyAccessor = GetPropertyAccessor(property);
×
70
        return OutputIf(propertyAccessor.Column, tableAlias, columnAlias, condition);
×
71
    }
72

73
    /// <summary>
74
    /// Sets the target table for the DELETE statement using the entity's mapping information by default.
75
    /// </summary>
76
    /// <param name="tableName">The name of the table (optional, defaults to entity mapping).</param>
77
    /// <param name="tableSchema">The schema of the table (optional, defaults to entity mapping).</param>
78
    /// <param name="tableAlias">The alias for the table (optional).</param>
79
    /// <returns>
80
    /// The same builder instance for method chaining.
81
    /// </returns>
82
    public override DeleteEntityBuilder<TEntity> From(
83
        string? tableName = null,
84
        string? tableSchema = null,
85
        string? tableAlias = null)
86
    {
87
        return base.From(
1✔
88
            tableName ?? _typeAccessor.TableName,
1✔
89
            tableSchema ?? _typeAccessor.TableSchema,
1✔
90
            tableAlias);
1✔
91
    }
92

93
    /// <summary>
94
    /// Adds a JOIN clause to the DELETE statement using the specified builder action for the right entity.
95
    /// </summary>
96
    /// <typeparam name="TRight">The type of the right join entity.</typeparam>
97
    /// <param name="builder">An action that configures the join using a <see cref="JoinEntityBuilder{TEntity, TRight}"/>.</param>
98
    /// <returns>
99
    /// The same builder instance for method chaining.
100
    /// </returns>
101
    public DeleteEntityBuilder<TEntity> Join<TRight>(Action<JoinEntityBuilder<TEntity, TRight>> builder)
102
        where TRight : class
103
    {
104
        var innerBuilder = new JoinEntityBuilder<TEntity, TRight>(QueryGenerator, Parameters);
1✔
105
        builder(innerBuilder);
1✔
106

107
        JoinExpressions.Add(innerBuilder.BuildExpression());
1✔
108

109
        return this;
1✔
110
    }
111

112
    /// <summary>
113
    /// Adds a JOIN clause to the DELETE statement using the specified builder action for the left and right entities.
114
    /// </summary>
115
    /// <typeparam name="TLeft">The type of the left join entity.</typeparam>
116
    /// <typeparam name="TRight">The type of the right join entity.</typeparam>
117
    /// <param name="builder">An action that configures the join using a <see cref="JoinEntityBuilder{TLeft, TRight}"/>.</param>
118
    /// <returns>
119
    /// The same builder instance for method chaining.
120
    /// </returns>
121
    public DeleteEntityBuilder<TEntity> Join<TLeft, TRight>(Action<JoinEntityBuilder<TLeft, TRight>> builder)
122
        where TLeft : class
123
        where TRight : class
124
    {
125
        var innerBuilder = new JoinEntityBuilder<TLeft, TRight>(QueryGenerator, Parameters);
×
126
        builder(innerBuilder);
×
127

128
        JoinExpressions.Add(innerBuilder.BuildExpression());
×
129

130
        return this;
×
131
    }
132

133
    /// <summary>
134
    /// Adds a WHERE clause for the specified property, value, and filter operator.
135
    /// </summary>
136
    /// <typeparam name="TValue">The type of the value to compare.</typeparam>
137
    /// <param name="property">An expression selecting the property to filter on.</param>
138
    /// <param name="parameterValue">The value to compare the property against.</param>
139
    /// <param name="filterOperator">The filter operator to use (default is <see cref="FilterOperators.Equal"/>).</param>
140
    /// <returns>
141
    /// The same builder instance for method chaining.
142
    /// </returns>
143
    public DeleteEntityBuilder<TEntity> Where<TValue>(
144
        Expression<Func<TEntity, TValue>> property,
145
        TValue? parameterValue,
146
        FilterOperators filterOperator = FilterOperators.Equal)
147
    {
148
        return Where<TValue>(property, parameterValue, null, filterOperator);
5✔
149
    }
150

151
    /// <summary>
152
    /// Adds a WHERE clause for the specified property, value, filter operator, and table alias.
153
    /// </summary>
154
    /// <typeparam name="TValue">The type of the value to compare.</typeparam>
155
    /// <param name="property">An expression selecting the property to filter on.</param>
156
    /// <param name="parameterValue">The value to compare the property against.</param>
157
    /// <param name="tableAlias">The table alias to use in the query.</param>
158
    /// <param name="filterOperator">The filter operator to use (default is <see cref="FilterOperators.Equal"/>).</param>
159
    /// <returns>
160
    /// The same builder instance for method chaining.
161
    /// </returns>
162
    public DeleteEntityBuilder<TEntity> Where<TValue>(
163
        Expression<Func<TEntity, TValue>> property,
164
        TValue? parameterValue,
165
        string? tableAlias,
166
        FilterOperators filterOperator = FilterOperators.Equal)
167
    {
168
        var propertyAccessor = GetPropertyAccessor(property);
5✔
169

170
        return Where(propertyAccessor.Column, parameterValue, tableAlias, filterOperator);
5✔
171
    }
172

173
    /// <summary>
174
    /// Adds a WHERE clause for the specified model property, value, filter operator, and table alias.
175
    /// </summary>
176
    /// <typeparam name="TModel">The type of the model.</typeparam>
177
    /// <typeparam name="TValue">The type of the value to compare.</typeparam>
178
    /// <param name="property">An expression selecting the property to filter on.</param>
179
    /// <param name="parameterValue">The value to compare the property against.</param>
180
    /// <param name="tableAlias">The table alias to use in the query.</param>
181
    /// <param name="filterOperator">The filter operator to use (default is <see cref="FilterOperators.Equal"/>).</param>
182
    /// <returns>
183
    /// The same builder instance for method chaining.
184
    /// </returns>
185
    public DeleteEntityBuilder<TEntity> Where<TModel, TValue>(
186
        Expression<Func<TModel, TValue>> property,
187
        TValue parameterValue,
188
        string? tableAlias,
189
        FilterOperators filterOperator = FilterOperators.Equal)
190
    {
191
        var typeAccessor = TypeAccessor.GetAccessor<TModel>();
1✔
192
        var propertyAccessor = GetPropertyAccessor(typeAccessor, property);
1✔
193

194
        return Where(propertyAccessor.Column, parameterValue, tableAlias, filterOperator);
1✔
195
    }
196

197
    /// <summary>
198
    /// Conditionally adds a WHERE clause for the specified property, value, and filter operator.
199
    /// </summary>
200
    /// <typeparam name="TValue">The type of the value to compare.</typeparam>
201
    /// <param name="property">An expression selecting the property to filter on.</param>
202
    /// <param name="parameterValue">The value to compare the property against.</param>
203
    /// <param name="filterOperator">The filter operator to use (default is <see cref="FilterOperators.Equal"/>).</param>
204
    /// <param name="condition">A function that determines whether to add the clause, based on the property name and value. If <c>null</c>, the clause is always added.</param>
205
    /// <returns>
206
    /// The same builder instance for method chaining.
207
    /// </returns>
208
    public DeleteEntityBuilder<TEntity> WhereIf<TValue>(
209
        Expression<Func<TEntity, TValue>> property,
210
        TValue? parameterValue,
211
        FilterOperators filterOperator = FilterOperators.Equal,
212
        Func<string, TValue?, bool>? condition = null)
213
    {
214
        return WhereIf(property, parameterValue, null, filterOperator, condition);
×
215
    }
216

217
    /// <summary>
218
    /// Conditionally adds a WHERE clause for the specified property, value, filter operator, and table alias.
219
    /// </summary>
220
    /// <typeparam name="TValue">The type of the value to compare.</typeparam>
221
    /// <param name="property">An expression selecting the property to filter on.</param>
222
    /// <param name="parameterValue">The value to compare the property against.</param>
223
    /// <param name="tableAlias">The table alias to use in the query.</param>
224
    /// <param name="filterOperator">The filter operator to use (default is <see cref="FilterOperators.Equal"/>).</param>
225
    /// <param name="condition">A function that determines whether to add the clause, based on the table alias and value. If <c>null</c>, the clause is always added.</param>
226
    /// <returns>
227
    /// The same builder instance for method chaining.
228
    /// </returns>
229
    public DeleteEntityBuilder<TEntity> WhereIf<TValue>(
230
        Expression<Func<TEntity, TValue>> property,
231
        TValue? parameterValue,
232
        string? tableAlias,
233
        FilterOperators filterOperator = FilterOperators.Equal,
234
        Func<string, TValue?, bool>? condition = null)
235
    {
NEW
236
        var propertyAccessor = GetPropertyAccessor(property);
×
237

238
        return WhereIf(propertyAccessor.Column, parameterValue, tableAlias, filterOperator, condition);
×
239
    }
240

241
    /// <summary>
242
    /// Adds a WHERE IN clause for the specified property and collection of values, with an optional table alias.
243
    /// </summary>
244
    /// <typeparam name="TValue">The type of the values to compare.</typeparam>
245
    /// <param name="property">An expression selecting the property to filter on.</param>
246
    /// <param name="parameterValues">The collection of values for the IN clause.</param>
247
    /// <param name="tableAlias">The table alias to use in the query (optional).</param>
248
    /// <returns>
249
    /// The same builder instance for method chaining.
250
    /// </returns>
251
    public DeleteEntityBuilder<TEntity> WhereIn<TValue>(
252
        Expression<Func<TEntity, TValue>> property,
253
        IEnumerable<TValue> parameterValues,
254
        string? tableAlias = null)
255
    {
NEW
256
        var propertyAccessor = GetPropertyAccessor(property);
×
257

NEW
258
        return WhereIn(propertyAccessor.Column, parameterValues, tableAlias);
×
259
    }
260

261
    /// <summary>
262
    /// Conditionally adds a WHERE IN clause for the specified property and collection of values.
263
    /// </summary>
264
    /// <typeparam name="TValue">The type of the values to compare.</typeparam>
265
    /// <param name="property">An expression selecting the property to filter on.</param>
266
    /// <param name="parameterValues">The collection of values for the IN clause.</param>
267
    /// <param name="condition">A function that determines whether to add the clause, based on the property name and values. If <c>null</c>, the clause is always added.</param>
268
    /// <returns>
269
    /// The same builder instance for method chaining.
270
    /// </returns>
271
    public DeleteEntityBuilder<TEntity> WhereInIf<TValue>(
272
        Expression<Func<TEntity, TValue>> property,
273
        IEnumerable<TValue> parameterValues,
274
        Func<string, IEnumerable<TValue>, bool>? condition = null)
275
    {
NEW
276
        var propertyAccessor = GetPropertyAccessor(property);
×
277

NEW
278
        return WhereInIf(propertyAccessor.Column, parameterValues, condition);
×
279
    }
280

281
    /// <summary>
282
    /// Conditionally adds a WHERE IN clause for the specified property, collection of values, and table alias.
283
    /// </summary>
284
    /// <typeparam name="TValue">The type of the values to compare.</typeparam>
285
    /// <param name="property">An expression selecting the property to filter on.</param>
286
    /// <param name="parameterValues">The collection of values for the IN clause.</param>
287
    /// <param name="tableAlias">The table alias to use in the query.</param>
288
    /// <param name="condition">A function that determines whether to add the clause, based on the table alias and values. If <c>null</c>, the clause is always added.</param>
289
    /// <returns>
290
    /// The same builder instance for method chaining.
291
    /// </returns>
292
    public DeleteEntityBuilder<TEntity> WhereInIf<TValue>(
293
        Expression<Func<TEntity, TValue>> property,
294
        IEnumerable<TValue> parameterValues,
295
        string? tableAlias,
296
        Func<string, IEnumerable<TValue>, bool>? condition = null)
297
    {
NEW
298
        var propertyAccessor = GetPropertyAccessor(property);
×
299

NEW
300
        return WhereInIf(propertyAccessor.Column, parameterValues, tableAlias, condition);
×
301
    }
302

303
    /// <summary>
304
    /// Adds a logical OR group to the WHERE clause using the specified builder action.
305
    /// </summary>
306
    /// <param name="builder">An action that configures the logical OR group using a <see cref="LogicalEntityBuilder{TEntity}"/>.</param>
307
    /// <returns>
308
    /// The same builder instance for method chaining.
309
    /// </returns>
310
    public DeleteEntityBuilder<TEntity> WhereOr(Action<LogicalEntityBuilder<TEntity>> builder)
311
    {
312
        var innerBuilder = new LogicalEntityBuilder<TEntity>(QueryGenerator, Parameters, LogicalOperators.Or);
×
313

314
        builder(innerBuilder);
×
315

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

NEW
318
        if (statement?.Statement.HasValue() == true)
×
319
            WhereExpressions.Add(new WhereExpression(statement.Statement, IsRaw: true));
×
320

321
        return this;
×
322
    }
323

324
    /// <summary>
325
    /// Adds a logical AND group to the WHERE clause using the specified builder action.
326
    /// </summary>
327
    /// <param name="builder">An action that configures the logical AND group using a <see cref="LogicalEntityBuilder{TEntity}"/>.</param>
328
    /// <returns>
329
    /// The same builder instance for method chaining.
330
    /// </returns>
331
    public DeleteEntityBuilder<TEntity> WhereAnd(Action<LogicalEntityBuilder<TEntity>> builder)
332
    {
333
        var innerBuilder = new LogicalEntityBuilder<TEntity>(QueryGenerator, Parameters, LogicalOperators.And);
×
334

335
        builder(innerBuilder);
×
336

337
        var statement = innerBuilder.BuildStatement();
×
338

NEW
339
        if (statement?.Statement.HasValue() == true)
×
340
            WhereExpressions.Add(new WhereExpression(statement.Statement, IsRaw: true));
×
341

342
        return this;
×
343
    }
344

345
    /// <summary>
346
    /// Builds the SQL DELETE statement using the current configuration.
347
    /// </summary>
348
    /// <returns>
349
    /// A <see cref="QueryStatement"/> containing the SQL DELETE statement and its parameters.
350
    /// </returns>
351
    public override QueryStatement? BuildStatement()
352
    {
353
        // add table and schema from attribute if not set
354
        if (TableExpression == null)
6✔
355
            Table(_typeAccessor.TableName, _typeAccessor.TableSchema);
6✔
356

357
        return base.BuildStatement();
6✔
358
    }
359

360

361
    private static IMemberAccessor GetPropertyAccessor<TModel, TValue>(
362
        TypeAccessor typeAccessor,
363
        Expression<Func<TModel, TValue>> property)
364
    {
365
        if (property is null)
12!
NEW
366
            throw new ArgumentNullException(nameof(property));
×
367

368
        var propertyAccessor = typeAccessor.FindProperty(property);
12✔
369
        if (propertyAccessor is null)
12!
NEW
370
            throw new ArgumentException("The specified property does not exist on the entity.", nameof(property));
×
371

372
        return propertyAccessor;
12✔
373
    }
374

375
    private static IMemberAccessor GetPropertyAccessor<TValue>(Expression<Func<TEntity, TValue>> property)
376
        => GetPropertyAccessor(_typeAccessor, property);
11✔
377

378
}
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