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

loresoft / EntityFrameworkCore.Generator / 15303740763

28 May 2025 03:07PM UTC coverage: 54.841% (-0.2%) from 55.036%
15303740763

push

github

pwelter34
add file header support

642 of 1331 branches covered (48.23%)

Branch coverage included in aggregate %.

14 of 33 new or added lines in 14 files covered. (42.42%)

3 existing lines in 3 files now uncovered.

1873 of 3255 relevant lines covered (57.54%)

61.12 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

NEW
22
        if (Options.Data.Query.Header.HasValue())
×
NEW
23
            CodeBuilder.AppendLine(Options.Data.Query.Header).AppendLine();
×
24

25
        CodeBuilder.AppendLine("using System;");
×
26
        CodeBuilder.AppendLine("using System.Collections.Generic;");
×
27
        CodeBuilder.AppendLine("using System.Linq;");
×
28
        CodeBuilder.AppendLine("using System.Threading;");
×
29
        CodeBuilder.AppendLine("using System.Threading.Tasks;");
×
NEW
30
        CodeBuilder.AppendLine();
×
31
        CodeBuilder.AppendLine("using Microsoft.EntityFrameworkCore;");
×
32
        CodeBuilder.AppendLine();
×
33

34
        var extensionNamespace = Options.Data.Query.Namespace;
×
35

36
        CodeBuilder.Append($"namespace {extensionNamespace}");
×
37

38
        if (Options.Project.FileScopedNamespace)
×
39
        {
40
            CodeBuilder.AppendLine(";");
×
41
            CodeBuilder.AppendLine();
×
42
            GenerateClass();
×
43
        }
44
        else
45
        {
46
            CodeBuilder.AppendLine();
×
47
            CodeBuilder.AppendLine("{");
×
48

49
            using (CodeBuilder.Indent())
×
50
            {
51
                GenerateClass();
×
52
            }
×
53

54
            CodeBuilder.AppendLine("}");
×
55
        }
56

57
        return CodeBuilder.ToString();
×
58
    }
59

60

61
    private void GenerateClass()
62
    {
63
        var entityClass = _entity.EntityClass.ToSafeName();
×
64
        string safeName = _entity.EntityNamespace + "." + entityClass;
×
65

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

73
        CodeBuilder.AppendLine($"public static partial class {entityClass}Extensions");
×
74
        CodeBuilder.AppendLine("{");
×
75

76
        using (CodeBuilder.Indent())
×
77
        {
78
            GenerateMethods();
×
79
        }
×
80

81
        CodeBuilder.AppendLine("}");
×
82

83
    }
×
84

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

108
    }
×
109

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

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

126
        CodeBuilder.Append($"public static System.Linq.IQueryable<{safeName}> {prefix}{suffix}(this System.Linq.IQueryable<{safeName}> queryable, ");
×
127
        AppendParameters(method);
×
128
        CodeBuilder.AppendLine(")");
×
129
        CodeBuilder.AppendLine("{");
×
130

131
        using (CodeBuilder.Indent())
×
132
        {
133
            CodeBuilder.AppendLine("if (queryable is null)");
×
134
            using (CodeBuilder.Indent())
×
135
                CodeBuilder.AppendLine("throw new ArgumentNullException(nameof(queryable));");
×
136
            CodeBuilder.AppendLine();
×
137

138
            CodeBuilder.Append("return queryable.Where(");
×
139
            AppendLamba(method);
×
140
            CodeBuilder.AppendLine(");");
×
141
        }
×
142

143
        CodeBuilder.AppendLine("}");
×
144
        CodeBuilder.AppendLine();
×
145
    }
×
146

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

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

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

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

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

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

176
        if (async)
×
177
            CodeBuilder.Append(", System.Threading.CancellationToken cancellationToken = default");
×
178

179
        CodeBuilder.AppendLine(")");
×
180
        CodeBuilder.AppendLine("{");
×
181

182
        using (CodeBuilder.Indent())
×
183
        {
184
            CodeBuilder.AppendLine("if (queryable is null)");
×
185
            using (CodeBuilder.Indent())
×
186
                CodeBuilder.AppendLine("throw new ArgumentNullException(nameof(queryable));");
×
187
            CodeBuilder.AppendLine();
×
188

189

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

192
            AppendLamba(method);
×
193

194
            if (async)
×
195
                CodeBuilder.Append(", cancellationToken");
×
196

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

200
        CodeBuilder.AppendLine("}");
×
201
        CodeBuilder.AppendLine();
×
202
    }
×
203

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

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

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

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

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

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

232
        if (async)
×
233
            CodeBuilder.Append(", System.Threading.CancellationToken cancellationToken = default");
×
234

235
        CodeBuilder.AppendLine(")");
×
236
        CodeBuilder.AppendLine("{");
×
237

238
        using (CodeBuilder.Indent())
×
239
        {
240
            CodeBuilder.AppendLine("if (queryable is null)");
×
241
            using (CodeBuilder.Indent())
×
242
                CodeBuilder.AppendLine("throw new ArgumentNullException(nameof(queryable));");
×
243
            CodeBuilder.AppendLine();
×
244

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

250
                if (async)
×
251
                    CodeBuilder.Append("new object[] { ");
×
252

253
                AppendNames(method);
×
254

255
                if (async)
×
256
                    CodeBuilder.Append(" }, cancellationToken");
×
257

258
                CodeBuilder.AppendLine(");");
×
259
            }
×
260

261
            CodeBuilder.AppendLine("");
×
262
            CodeBuilder.Append($"return {awaitPrefix}queryable.FirstOrDefault{asyncSuffix}(");
×
263

264
            AppendLamba(method);
×
265

266
            if (async)
×
267
                CodeBuilder.Append(", cancellationToken");
×
268

269
            CodeBuilder.AppendLine(");");
×
270
        }
×
271
        CodeBuilder.AppendLine("}");
×
272
        CodeBuilder.AppendLine();
×
273
    }
×
274

275

276
    private void AppendDocumentation(Method method)
277
    {
278
        foreach (var property in method.Properties)
×
279
        {
280
            string paramName = property.PropertyName
×
281
                .ToCamelCase()
×
282
                .ToSafeName();
×
283

284
            CodeBuilder.AppendLine($"/// <param name=\"{paramName}\">The value to filter by.</param>");
×
285
        }
286
    }
×
287

288
    private void AppendParameters(Method method)
289
    {
290
        bool wrote = false;
×
291

292
        foreach (var property in method.Properties)
×
293
        {
294
            if (wrote)
×
295
                CodeBuilder.Append(", ");
×
296

297
            var paramName = property.PropertyName
×
298
                .ToCamelCase()
×
299
                .ToSafeName();
×
300

301
            var paramType = property.SystemType
×
302
                .ToNullableType(property.IsNullable == true);
×
303

304
            CodeBuilder.Append($"{paramType} {paramName}");
×
305

306
            wrote = true;
×
307
        }
308
    }
×
309

310
    private void AppendNames(Method method)
311
    {
312
        bool wrote = false;
×
313
        foreach (var property in method.Properties)
×
314
        {
315
            if (wrote)
×
316
                CodeBuilder.Append(", ");
×
317

318
            string paramName = property.PropertyName
×
319
                .ToCamelCase()
×
320
                .ToSafeName();
×
321

322
            CodeBuilder.Append(paramName);
×
323
            wrote = true;
×
324
        }
325
    }
×
326

327
    private void AppendLamba(Method method)
328
    {
329
        bool wrote = false;
×
330
        bool indented = false;
×
331

332
        foreach (var property in method.Properties)
×
333
        {
334
            string paramName = property.PropertyName
×
335
                .ToCamelCase()
×
336
                .ToSafeName();
×
337

338
            if (!wrote)
×
339
            {
340
                CodeBuilder.Append("q => ");
×
341
            }
342
            else
343
            {
344
                CodeBuilder.AppendLine();
×
345
                CodeBuilder.IncrementIndent();
×
346
                CodeBuilder.Append("&& ");
×
347

348
                indented = true;
×
349
            }
350

351
            if (property.IsNullable == true)
×
352
                CodeBuilder.Append($"(q.{property.PropertyName} == {paramName} || ({paramName} == null && q.{property.PropertyName} == null))");
×
353
            else
354
                CodeBuilder.Append($"q.{property.PropertyName} == {paramName}");
×
355

356
            wrote = true;
×
357
        }
358

359
        if (indented)
×
360
            CodeBuilder.DecrementIndent();
×
361
    }
×
362
}
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