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

loresoft / EntityFrameworkCore.Generator / 15072048022

16 May 2025 03:31PM UTC coverage: 55.392% (-1.4%) from 56.772%
15072048022

push

github

pwelter34
enable nullable support

616 of 1271 branches covered (48.47%)

Branch coverage included in aggregate %.

233 of 397 new or added lines in 61 files covered. (58.69%)

17 existing lines in 11 files now uncovered.

1824 of 3134 relevant lines covered (58.2%)

88.56 hits per line

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

0.0
/src/EntityFrameworkCore.Generator.Core/Templates/QueryExtensionTemplate.cs
1
using System.Linq;
2

3
using EntityFrameworkCore.Generator.Extensions;
4
using EntityFrameworkCore.Generator.Metadata.Generation;
5
using EntityFrameworkCore.Generator.Options;
6

7
namespace EntityFrameworkCore.Generator.Templates;
8

9
public class QueryExtensionTemplate : CodeTemplateBase
10
{
11
    private readonly Entity _entity;
12

13
    public QueryExtensionTemplate(Entity entity, GeneratorOptions options) : base(options)
×
14
    {
15
        _entity = entity;
×
16
    }
×
17

18
    public override string WriteCode()
19
    {
20
        CodeBuilder.Clear();
×
21

22
        CodeBuilder.AppendLine("using System;");
×
23
        CodeBuilder.AppendLine("using System.Collections.Generic;");
×
24
        CodeBuilder.AppendLine("using System.Linq;");
×
25
        CodeBuilder.AppendLine("using System.Threading;");
×
26
        CodeBuilder.AppendLine("using System.Threading.Tasks;");
×
27
        CodeBuilder.AppendLine("using Microsoft.EntityFrameworkCore;");
×
28
        CodeBuilder.AppendLine();
×
29

30
        var extensionNamespace = Options.Data.Query.Namespace;
×
31

32
        CodeBuilder.Append($"namespace {extensionNamespace}");
×
33

34
        if (Options.Project.FileScopedNamespace)
×
35
        {
36
            CodeBuilder.AppendLine(";");
×
37
            CodeBuilder.AppendLine();
×
38
            GenerateClass();
×
39
        }
40
        else
41
        {
42
            CodeBuilder.AppendLine();
×
43
            CodeBuilder.AppendLine("{");
×
44

45
            using (CodeBuilder.Indent())
×
46
            {
47
                GenerateClass();
×
48
            }
×
49

50
            CodeBuilder.AppendLine("}");
×
51
        }
52

53
        return CodeBuilder.ToString();
×
54
    }
55

56

57
    private void GenerateClass()
58
    {
59
        var entityClass = _entity.EntityClass.ToSafeName();
×
60
        string safeName = _entity.EntityNamespace + "." + entityClass;
×
61

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

69
        CodeBuilder.AppendLine($"public static partial class {entityClass}Extensions");
×
70
        CodeBuilder.AppendLine("{");
×
71

72
        using (CodeBuilder.Indent())
×
73
        {
74
            GenerateMethods();
×
75
        }
×
76

77
        CodeBuilder.AppendLine("}");
×
78

79
    }
×
80

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

104
    }
×
105

106
    private void GenerateMethod(Method method)
107
    {
NEW
108
        var safeName = _entity.EntityNamespace + "." + _entity.EntityClass.ToSafeName();
×
NEW
109
        var prefix = Options.Data.Query.IndexPrefix;
×
NEW
110
        var suffix = method.NameSuffix;
×
111

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

122
        CodeBuilder.Append($"public static System.Linq.IQueryable<{safeName}> {prefix}{suffix}(this System.Linq.IQueryable<{safeName}> queryable, ");
×
123
        AppendParameters(method);
×
124
        CodeBuilder.AppendLine(")");
×
125
        CodeBuilder.AppendLine("{");
×
126

127
        using (CodeBuilder.Indent())
×
128
        {
129
            CodeBuilder.AppendLine("if (queryable is null)");
×
130
            using (CodeBuilder.Indent())
×
131
                CodeBuilder.AppendLine("throw new ArgumentNullException(nameof(queryable));");
×
132
            CodeBuilder.AppendLine();
×
133

NEW
134
            CodeBuilder.Append("return queryable.Where(");
×
135
            AppendLamba(method);
×
136
            CodeBuilder.AppendLine(");");
×
137
        }
×
138

139
        CodeBuilder.AppendLine("}");
×
140
        CodeBuilder.AppendLine();
×
141
    }
×
142

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

NEW
149
        var asyncSuffix = async ? "Async" : string.Empty;
×
NEW
150
        var asyncPrefix = async ? "async " : string.Empty;
×
NEW
151
        var awaitPrefix = async ? "await " : string.Empty;
×
NEW
152
        var nullableSuffix = Options.Project.Nullable ? "?" : "";
×
NEW
153
        var returnType = async ? $"System.Threading.Tasks.Task<{safeName}{nullableSuffix}>" : safeName + nullableSuffix;
×
154

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

163
            if (async)
×
164
                CodeBuilder.AppendLine("/// <param name=\"cancellationToken\">A <see cref=\"System.Threading.CancellationToken\" /> to observe while waiting for the task to complete.</param>");
×
165

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

169
        CodeBuilder.Append($"public static {asyncPrefix}{returnType} {uniquePrefix}{suffix}{asyncSuffix}(this System.Linq.IQueryable<{safeName}> queryable, ");
×
170
        AppendParameters(method);
×
171

172
        if (async)
×
173
            CodeBuilder.Append(", System.Threading.CancellationToken cancellationToken = default");
×
174

175
        CodeBuilder.AppendLine(")");
×
176
        CodeBuilder.AppendLine("{");
×
177

178
        using (CodeBuilder.Indent())
×
179
        {
180
            CodeBuilder.AppendLine("if (queryable is null)");
×
181
            using (CodeBuilder.Indent())
×
182
                CodeBuilder.AppendLine("throw new ArgumentNullException(nameof(queryable));");
×
183
            CodeBuilder.AppendLine();
×
184

185

186
            CodeBuilder.Append($"return {awaitPrefix}queryable.FirstOrDefault{asyncSuffix}(");
×
187

188
            AppendLamba(method);
×
189

190
            if (async)
×
191
                CodeBuilder.Append(", cancellationToken");
×
192

193
            CodeBuilder.AppendLine(");");
×
194
        }
×
195

196
        CodeBuilder.AppendLine("}");
×
197
        CodeBuilder.AppendLine();
×
198
    }
×
199

200
    private void GenerateKeyMethod(Method method, bool async = false)
201
    {
NEW
202
        var safeName = _entity.EntityNamespace + "." + _entity.EntityClass.ToSafeName();
×
NEW
203
        var uniquePrefix = Options.Data.Query.UniquePrefix;
×
204

NEW
205
        var asyncSuffix = async ? "Async" : string.Empty;
×
NEW
206
        var asyncPrefix = async ? "async " : string.Empty;
×
NEW
207
        var awaitPrefix = async ? "await " : string.Empty;
×
NEW
208
        var nullableSuffix = Options.Project.Nullable ? "?" : "";
×
NEW
209
        var returnType = async ? $"System.Threading.Tasks.ValueTask<{safeName}{nullableSuffix}>" : safeName + nullableSuffix;
×
210

211
        if (Options.Data.Query.Document)
×
212
        {
213
            CodeBuilder.AppendLine("/// <summary>");
×
214
            CodeBuilder.AppendLine("/// Gets an instance by the primary key.");
×
215
            CodeBuilder.AppendLine("/// </summary>");
×
216
            CodeBuilder.AppendLine("/// <param name=\"queryable\">An <see cref=\"T:System.Linq.IQueryable`1\" /> to filter.</param>");
×
217
            AppendDocumentation(method);
×
218

219
            if (async)
×
220
                CodeBuilder.AppendLine("/// <param name=\"cancellationToken\">A <see cref=\"System.Threading.CancellationToken\" /> to observe while waiting for the task to complete.</param>");
×
221

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

225
        CodeBuilder.Append($"public static {asyncPrefix}{returnType} {uniquePrefix}Key{asyncSuffix}(this System.Linq.IQueryable<{safeName}> queryable, ");
×
226
        AppendParameters(method);
×
227

228
        if (async)
×
229
            CodeBuilder.Append(", System.Threading.CancellationToken cancellationToken = default");
×
230

231
        CodeBuilder.AppendLine(")");
×
232
        CodeBuilder.AppendLine("{");
×
233

234
        using (CodeBuilder.Indent())
×
235
        {
236
            CodeBuilder.AppendLine("if (queryable is null)");
×
237
            using (CodeBuilder.Indent())
×
238
                CodeBuilder.AppendLine("throw new ArgumentNullException(nameof(queryable));");
×
239
            CodeBuilder.AppendLine();
×
240

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

246
                if (async)
×
247
                    CodeBuilder.Append("new object[] { ");
×
248

249
                AppendNames(method);
×
250

251
                if (async)
×
252
                    CodeBuilder.Append(" }, cancellationToken");
×
253

254
                CodeBuilder.AppendLine(");");
×
255
            }
×
256

257
            CodeBuilder.AppendLine("");
×
258
            CodeBuilder.Append($"return {awaitPrefix}queryable.FirstOrDefault{asyncSuffix}(");
×
259

260
            AppendLamba(method);
×
261

262
            if (async)
×
263
                CodeBuilder.Append(", cancellationToken");
×
264

265
            CodeBuilder.AppendLine(");");
×
266
        }
×
267
        CodeBuilder.AppendLine("}");
×
268
        CodeBuilder.AppendLine();
×
269
    }
×
270

271

272
    private void AppendDocumentation(Method method)
273
    {
274
        foreach (var property in method.Properties)
×
275
        {
276
            string paramName = property.PropertyName
×
277
                .ToCamelCase()
×
278
                .ToSafeName();
×
279

280
            CodeBuilder.AppendLine($"/// <param name=\"{paramName}\">The value to filter by.</param>");
×
281
        }
282
    }
×
283

284
    private void AppendParameters(Method method)
285
    {
286
        bool wrote = false;
×
287

288
        foreach (var property in method.Properties)
×
289
        {
290
            if (wrote)
×
291
                CodeBuilder.Append(", ");
×
292

NEW
293
            var paramName = property.PropertyName
×
294
                .ToCamelCase()
×
295
                .ToSafeName();
×
296

NEW
297
            var paramType = property.SystemType
×
298
                .ToNullableType(property.IsNullable == true);
×
299

300
            CodeBuilder.Append($"{paramType} {paramName}");
×
301

302
            wrote = true;
×
303
        }
304
    }
×
305

306
    private void AppendNames(Method method)
307
    {
308
        bool wrote = false;
×
309
        foreach (var property in method.Properties)
×
310
        {
311
            if (wrote)
×
312
                CodeBuilder.Append(", ");
×
313

314
            string paramName = property.PropertyName
×
315
                .ToCamelCase()
×
316
                .ToSafeName();
×
317

318
            CodeBuilder.Append(paramName);
×
319
            wrote = true;
×
320
        }
321
    }
×
322

323
    private void AppendLamba(Method method)
324
    {
325
        bool wrote = false;
×
326
        bool indented = false;
×
327

328
        foreach (var property in method.Properties)
×
329
        {
330
            string paramName = property.PropertyName
×
331
                .ToCamelCase()
×
332
                .ToSafeName();
×
333

334
            if (!wrote)
×
335
            {
336
                CodeBuilder.Append("q => ");
×
337
            }
338
            else
339
            {
340
                CodeBuilder.AppendLine();
×
341
                CodeBuilder.IncrementIndent();
×
342
                CodeBuilder.Append("&& ");
×
343

344
                indented = true;
×
345
            }
346

347
            if (property.IsNullable == true)
×
348
                CodeBuilder.Append($"(q.{property.PropertyName} == {paramName} || ({paramName} == null && q.{property.PropertyName} == null))");
×
349
            else
350
                CodeBuilder.Append($"q.{property.PropertyName} == {paramName}");
×
351

352
            wrote = true;
×
353
        }
354

355
        if (indented)
×
356
            CodeBuilder.DecrementIndent();
×
357
    }
×
358
}
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

© 2025 Coveralls, Inc