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

loresoft / EntityFrameworkCore.Generator / 27730465225

18 Jun 2026 01:21AM UTC coverage: 74.693% (+19.8%) from 54.885%
27730465225

push

github

pwelter34
update tests

922 of 1609 branches covered (57.3%)

Branch coverage included in aggregate %.

7 of 7 new or added lines in 2 files covered. (100.0%)

230 existing lines in 23 files now uncovered.

4007 of 4990 relevant lines covered (80.3%)

1258.69 hits per line

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

94.66
/src/EntityFrameworkCore.Generator.Core/Templates/QueryExtensionTemplate.cs
1
using EntityFrameworkCore.Generator.Extensions;
2
using EntityFrameworkCore.Generator.Metadata.Generation;
3
using EntityFrameworkCore.Generator.Options;
4

5
namespace EntityFrameworkCore.Generator.Templates;
6

7
public class QueryExtensionTemplate : CodeTemplateBase
8
{
9
    private readonly Entity _entity;
10

11
    public QueryExtensionTemplate(Entity entity, GeneratorOptions options) : base(options)
137✔
12
    {
13
        _entity = entity;
137✔
14
    }
137✔
15

16
    public override string WriteCode()
17
    {
18
        CodeBuilder.Clear();
137✔
19

20
        if (Options.Data.Query.Header.HasValue())
137!
UNCOV
21
            CodeBuilder.AppendLine(Options.Data.Query.Header).AppendLine();
×
22

23
        CodeBuilder.AppendLine("using System;");
137✔
24
        CodeBuilder.AppendLine("using System.Collections.Generic;");
137✔
25
        CodeBuilder.AppendLine("using System.Linq;");
137✔
26
        CodeBuilder.AppendLine("using System.Threading;");
137✔
27
        CodeBuilder.AppendLine("using System.Threading.Tasks;");
137✔
28
        CodeBuilder.AppendLine();
137✔
29
        CodeBuilder.AppendLine("using Microsoft.EntityFrameworkCore;");
137✔
30
        CodeBuilder.AppendLine();
137✔
31

32
        var extensionNamespace = Options.Data.Query.Namespace;
137✔
33

34
        CodeBuilder.Append($"namespace {extensionNamespace}");
137✔
35

36
        if (Options.Project.FileScopedNamespace)
137!
37
        {
38
            CodeBuilder.AppendLine(";");
137✔
39
            CodeBuilder.AppendLine();
137✔
40
            GenerateClass();
137✔
41
        }
42
        else
43
        {
UNCOV
44
            CodeBuilder.AppendLine();
×
UNCOV
45
            CodeBuilder.AppendLine("{");
×
46

47
            using (CodeBuilder.Indent())
×
48
            {
49
                GenerateClass();
×
UNCOV
50
            }
×
51

52
            CodeBuilder.AppendLine("}");
×
53
        }
54

55
        return CodeBuilder.ToString();
137✔
56
    }
57

58

59
    private void GenerateClass()
60
    {
61
        var entityClass = _entity.EntityClass.ToSafeName();
137✔
62
        string safeName = _entity.EntityNamespace + "." + entityClass;
137✔
63

64
        if (Options.Data.Query.Document)
137!
65
        {
66
            CodeBuilder.AppendLine("/// <summary>");
137✔
67
            CodeBuilder.AppendLine($"/// Query extensions for entity <see cref=\"{safeName}\" />.");
137✔
68
            CodeBuilder.AppendLine("/// </summary>");
137✔
69
        }
70

71
        CodeBuilder.AppendLine($"public static partial class {entityClass}Extensions");
137✔
72
        CodeBuilder.AppendLine("{");
137✔
73

74
        using (CodeBuilder.Indent())
137✔
75
        {
76
            GenerateMethods();
137✔
77
        }
137✔
78

79
        CodeBuilder.AppendLine("}");
137✔
80

81
    }
137✔
82

83
    private void GenerateMethods()
84
    {
85
        CodeBuilder.AppendLine("#region Generated Extensions");
137✔
86
        foreach (var method in _entity.Methods.OrderBy(m => m.NameSuffix))
702✔
87
        {
88
            if (method.IsKey)
214✔
89
            {
90
                GenerateKeyMethod(method);
124✔
91
                GenerateKeyMethod(method, true);
124✔
92
            }
93
            else if (method.IsUnique)
90✔
94
            {
95
                GenerateUniqueMethod(method);
16✔
96
                GenerateUniqueMethod(method, true);
16✔
97
            }
98
            else
99
            {
100
                GenerateMethod(method);
74✔
101
            }
102
        }
103
        CodeBuilder.AppendLine("#endregion");
137✔
104
        CodeBuilder.AppendLine();
137✔
105

106
    }
137✔
107

108
    private void GenerateMethod(Method method)
109
    {
110
        var safeName = _entity.EntityNamespace + "." + _entity.EntityClass.ToSafeName();
74✔
111
        var prefix = Options.Data.Query.IndexPrefix;
74✔
112
        var suffix = method.NameSuffix;
74✔
113

114
        if (Options.Data.Query.Document)
74!
115
        {
116
            CodeBuilder.AppendLine("/// <summary>");
74✔
117
            CodeBuilder.AppendLine("/// Filters a sequence of values based on a predicate.");
74✔
118
            CodeBuilder.AppendLine("/// </summary>");
74✔
119
            CodeBuilder.AppendLine("/// <param name=\"queryable\">An <see cref=\"IQueryable`1\" /> to filter.</param>");
74✔
120
            AppendDocumentation(method);
74✔
121
            CodeBuilder.AppendLine("/// <returns>An <see cref=\"IQueryable`1\" /> that contains elements from the input sequence that satisfy the condition specified.</returns>");
74✔
122
        }
123

124
        CodeBuilder.Append($"public static IQueryable<{safeName}> {prefix}{suffix}(this IQueryable<{safeName}> queryable, ");
74✔
125
        AppendParameters(method);
74✔
126
        CodeBuilder.AppendLine(")");
74✔
127
        CodeBuilder.AppendLine("{");
74✔
128

129
        using (CodeBuilder.Indent())
74✔
130
        {
131
            CodeBuilder.AppendLine("if (queryable is null)");
74✔
132
            using (CodeBuilder.Indent())
74✔
133
                CodeBuilder.AppendLine("throw new ArgumentNullException(nameof(queryable));");
74✔
134
            CodeBuilder.AppendLine();
74✔
135

136
            CodeBuilder.Append("return queryable.Where(");
74✔
137
            AppendLamba(method);
74✔
138
            CodeBuilder.AppendLine(");");
74✔
139
        }
74✔
140

141
        CodeBuilder.AppendLine("}");
74✔
142
        CodeBuilder.AppendLine();
74✔
143
    }
74✔
144

145
    private void GenerateUniqueMethod(Method method, bool async = false)
146
    {
147
        var safeName = _entity.EntityNamespace + "." + _entity.EntityClass.ToSafeName();
32✔
148
        var uniquePrefix = Options.Data.Query.UniquePrefix;
32✔
149
        var suffix = method.NameSuffix;
32✔
150

151
        var asyncSuffix = async ? "Async" : string.Empty;
32✔
152
        var asyncPrefix = async ? "async " : string.Empty;
32✔
153
        var awaitPrefix = async ? "await " : string.Empty;
32✔
154
        var nullableSuffix = Options.Project.Nullable ? "?" : "";
32!
155
        var returnType = async ? $"System.Threading.Tasks.Task<{safeName}{nullableSuffix}>" : safeName + nullableSuffix;
32✔
156

157
        if (Options.Data.Query.Document)
32!
158
        {
159
            CodeBuilder.AppendLine("/// <summary>");
32✔
160
            CodeBuilder.AppendLine($"/// Gets an instance of <see cref=\"T:{safeName}\"/> by using a unique index.");
32✔
161
            CodeBuilder.AppendLine("/// </summary>");
32✔
162
            CodeBuilder.AppendLine("/// <param name=\"queryable\">An <see cref=\"IQueryable`1\" /> to filter.</param>");
32✔
163
            AppendDocumentation(method);
32✔
164

165
            if (async)
32✔
166
                CodeBuilder.AppendLine("/// <param name=\"cancellationToken\">A <see cref=\"CancellationToken\" /> to observe while waiting for the task to complete.</param>");
16✔
167

168
            CodeBuilder.AppendLine($"/// <returns>An instance of <see cref=\"T:{safeName}\"/> or null if not found.</returns>");
32✔
169
        }
170

171
        CodeBuilder.Append($"public static {asyncPrefix}{returnType} {uniquePrefix}{suffix}{asyncSuffix}(this IQueryable<{safeName}> queryable, ");
32✔
172
        AppendParameters(method);
32✔
173

174
        if (async)
32✔
175
            CodeBuilder.Append(", CancellationToken cancellationToken = default");
16✔
176

177
        CodeBuilder.AppendLine(")");
32✔
178
        CodeBuilder.AppendLine("{");
32✔
179

180
        using (CodeBuilder.Indent())
32✔
181
        {
182
            CodeBuilder.AppendLine("if (queryable is null)");
32✔
183
            using (CodeBuilder.Indent())
32✔
184
                CodeBuilder.AppendLine("throw new ArgumentNullException(nameof(queryable));");
32✔
185
            CodeBuilder.AppendLine();
32✔
186

187

188
            CodeBuilder.Append($"return {awaitPrefix}queryable.FirstOrDefault{asyncSuffix}(");
32✔
189

190
            AppendLamba(method);
32✔
191

192
            if (async)
32✔
193
                CodeBuilder.Append(", cancellationToken");
16✔
194

195
            CodeBuilder.AppendLine(");");
32✔
196
        }
32✔
197

198
        CodeBuilder.AppendLine("}");
32✔
199
        CodeBuilder.AppendLine();
32✔
200
    }
32✔
201

202
    private void GenerateKeyMethod(Method method, bool async = false)
203
    {
204
        var safeName = _entity.EntityNamespace + "." + _entity.EntityClass.ToSafeName();
248✔
205
        var uniquePrefix = Options.Data.Query.UniquePrefix;
248✔
206

207
        var asyncSuffix = async ? "Async" : string.Empty;
248✔
208
        var asyncPrefix = async ? "async " : string.Empty;
248✔
209
        var awaitPrefix = async ? "await " : string.Empty;
248✔
210
        var nullableSuffix = Options.Project.Nullable ? "?" : "";
248!
211
        var returnType = async ? $"System.Threading.Tasks.ValueTask<{safeName}{nullableSuffix}>" : safeName + nullableSuffix;
248✔
212

213
        if (Options.Data.Query.Document)
248!
214
        {
215
            CodeBuilder.AppendLine("/// <summary>");
248✔
216
            CodeBuilder.AppendLine("/// Gets an instance by the primary key.");
248✔
217
            CodeBuilder.AppendLine("/// </summary>");
248✔
218
            CodeBuilder.AppendLine("/// <param name=\"queryable\">An <see cref=\"IQueryable`1\" /> to filter.</param>");
248✔
219
            AppendDocumentation(method);
248✔
220

221
            if (async)
248✔
222
                CodeBuilder.AppendLine("/// <param name=\"cancellationToken\">A <see cref=\"CancellationToken\" /> to observe while waiting for the task to complete.</param>");
124✔
223

224
            CodeBuilder.AppendLine($"/// <returns>An instance of <see cref=\"T:{safeName}\"/> or null if not found.</returns>");
248✔
225
        }
226

227
        CodeBuilder.Append($"public static {asyncPrefix}{returnType} {uniquePrefix}Key{asyncSuffix}(this IQueryable<{safeName}> queryable, ");
248✔
228
        AppendParameters(method);
248✔
229

230
        if (async)
248✔
231
            CodeBuilder.Append(", CancellationToken cancellationToken = default");
124✔
232

233
        CodeBuilder.AppendLine(")");
248✔
234
        CodeBuilder.AppendLine("{");
248✔
235

236
        using (CodeBuilder.Indent())
248✔
237
        {
238
            CodeBuilder.AppendLine("if (queryable is null)");
248✔
239
            using (CodeBuilder.Indent())
248✔
240
                CodeBuilder.AppendLine("throw new ArgumentNullException(nameof(queryable));");
248✔
241
            CodeBuilder.AppendLine();
248✔
242

243
            CodeBuilder.AppendLine($"if (queryable is DbSet<{safeName}> dbSet)");
248✔
244
            using (CodeBuilder.Indent())
248✔
245
            {
246
                CodeBuilder.Append($"return {awaitPrefix}dbSet.Find{asyncSuffix}(");
248✔
247

248
                if (async)
248✔
249
                    CodeBuilder.Append("new object[] { ");
124✔
250

251
                AppendNames(method);
248✔
252

253
                if (async)
248✔
254
                    CodeBuilder.Append(" }, cancellationToken");
124✔
255

256
                CodeBuilder.AppendLine(");");
248✔
257
            }
248✔
258

259
            CodeBuilder.AppendLine("");
248✔
260
            CodeBuilder.Append($"return {awaitPrefix}queryable.FirstOrDefault{asyncSuffix}(");
248✔
261

262
            AppendLamba(method);
248✔
263

264
            if (async)
248✔
265
                CodeBuilder.Append(", cancellationToken");
124✔
266

267
            CodeBuilder.AppendLine(");");
248✔
268
        }
248✔
269
        CodeBuilder.AppendLine("}");
248✔
270
        CodeBuilder.AppendLine();
248✔
271
    }
248✔
272

273

274
    private void AppendDocumentation(Method method)
275
    {
276
        foreach (var property in method.Properties)
1,506✔
277
        {
278
            string paramName = property.PropertyName
399✔
279
                .ToCamelCase()
399✔
280
                .ToSafeName();
399✔
281

282
            CodeBuilder.AppendLine($"/// <param name=\"{paramName}\">The value to filter by.</param>");
399✔
283
        }
284
    }
354✔
285

286
    private void AppendParameters(Method method)
287
    {
288
        bool wrote = false;
354✔
289

290
        foreach (var property in method.Properties)
1,506✔
291
        {
292
            if (wrote)
399✔
293
                CodeBuilder.Append(", ");
45✔
294

295
            var paramName = property.PropertyName
399✔
296
                .ToCamelCase()
399✔
297
                .ToSafeName();
399✔
298

299
            var paramType = property.SystemType
399✔
300
                .ToNullableType(property.IsNullable == true);
399✔
301

302
            CodeBuilder.Append($"{paramType} {paramName}");
399✔
303

304
            wrote = true;
399✔
305
        }
306
    }
354✔
307

308
    private void AppendNames(Method method)
309
    {
310
        bool wrote = false;
248✔
311
        foreach (var property in method.Properties)
1,056✔
312
        {
313
            if (wrote)
280✔
314
                CodeBuilder.Append(", ");
32✔
315

316
            string paramName = property.PropertyName
280✔
317
                .ToCamelCase()
280✔
318
                .ToSafeName();
280✔
319

320
            CodeBuilder.Append(paramName);
280✔
321
            wrote = true;
280✔
322
        }
323
    }
248✔
324

325
    private void AppendLamba(Method method)
326
    {
327
        bool wrote = false;
354✔
328
        bool indented = false;
354✔
329

330
        foreach (var property in method.Properties)
1,506✔
331
        {
332
            string paramName = property.PropertyName
399✔
333
                .ToCamelCase()
399✔
334
                .ToSafeName();
399✔
335

336
            if (!wrote)
399✔
337
            {
338
                CodeBuilder.Append("q => ");
354✔
339
            }
340
            else
341
            {
342
                CodeBuilder.AppendLine();
45✔
343
                CodeBuilder.IncrementIndent();
45✔
344
                CodeBuilder.Append("&& ");
45✔
345

346
                indented = true;
45✔
347
            }
348

349
            if (property.IsNullable == true)
399✔
350
                CodeBuilder.Append($"(q.{property.PropertyName} == {paramName} || ({paramName} == null && q.{property.PropertyName} == null))");
18✔
351
            else
352
                CodeBuilder.Append($"q.{property.PropertyName} == {paramName}");
381✔
353

354
            wrote = true;
399✔
355
        }
356

357
        if (indented)
354✔
358
            CodeBuilder.DecrementIndent();
37✔
359
    }
354✔
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