• 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

65.96
/src/EntityFrameworkCore.Generator.Core/CodeGenerator.cs
1
using EntityFrameworkCore.Generator.Extensions;
2
using EntityFrameworkCore.Generator.Metadata.Generation;
3
using EntityFrameworkCore.Generator.Options;
4
using EntityFrameworkCore.Generator.Parsing;
5
using EntityFrameworkCore.Generator.Scripts;
6
using EntityFrameworkCore.Generator.Templates;
7

8
using Microsoft.Extensions.Logging;
9

10
using SchemaSaurus.Metadata;
11
using SchemaSaurus.Metadata.Provider;
12

13
namespace EntityFrameworkCore.Generator;
14

15
public class CodeGenerator : ICodeGenerator
16
{
17
    private readonly ILoggerFactory _loggerFactory;
18
    private readonly ILogger _logger;
19
    private readonly ModelGenerator _modelGenerator;
20
    private readonly SourceSynchronizer _synchronizer;
21

22
    public CodeGenerator(ILoggerFactory loggerFactory)
6✔
23
    {
24
        _loggerFactory = loggerFactory;
6✔
25
        _logger = loggerFactory.CreateLogger<CodeGenerator>();
6✔
26
        _modelGenerator = new ModelGenerator(loggerFactory);
6✔
27
        _synchronizer = new SourceSynchronizer(loggerFactory);
6✔
28
    }
6✔
29

30
    public GeneratorOptions Options { get; set; } = null!;
31

32
    public async Task<bool> GenerateAsync(GeneratorOptions options)
33
    {
34
        Options = options ?? throw new ArgumentNullException(nameof(options));
6!
35

36
        var databaseProvider = GetDatabaseProvider();
6✔
37
        var databaseModel = await GetDatabaseModel(databaseProvider);
6✔
38

39
        if (databaseModel == null)
6!
UNCOV
40
            throw new InvalidOperationException("Failed to create database model");
×
41

42
        _logger.LogInformation("Loaded database model for: {databaseName}", databaseModel.DatabaseName);
6✔
43

44
        var context = _modelGenerator.Generate(Options, databaseModel);
6✔
45

46
        _synchronizer.UpdateFromSource(context, options);
6✔
47

48
        GenerateFiles(context);
6✔
49

50
        return true;
6✔
51
    }
6✔
52

53
    private void GenerateFiles(EntityContext entityContext)
54
    {
55
        GenerateDataContext(entityContext);
6✔
56
        GenerateEntityClasses(entityContext);
6✔
57
        GenerateMappingClasses(entityContext);
6✔
58

59
        if (Options.Data.Query.Generate)
6✔
60
            GenerateQueryExtensions(entityContext);
4✔
61

62
        GenerateModelClasses(entityContext);
6✔
63

64
        GenerateScriptTemplates(entityContext);
6✔
65
    }
6✔
66

67
    private void GenerateQueryExtensions(EntityContext entityContext)
68
    {
69
        foreach (var entity in entityContext.Entities)
282✔
70
        {
71
            Options.Variables.Set(entity);
137✔
72

73
            var directory = Options.Data.Query.Directory ?? "Data\\Queries";
137!
74
            var file = entity.EntityClass + "Extensions.cs";
137✔
75
            var path = Path.Combine(directory, file);
137✔
76

77
            if (File.Exists(path))
137!
78
                _logger.LogInformation("Updating query extensions class: {file}", file);
×
79
            else
80
                _logger.LogInformation("Creating query extensions class: {file}", file);
137✔
81

82
            var template = new QueryExtensionTemplate(entity, Options);
137✔
83
            template.WriteCode(path);
137✔
84

85
            Options.Variables.Remove(entity);
137✔
86
        }
87
    }
4✔
88

89
    private void GenerateMappingClasses(EntityContext entityContext)
90
    {
91
        foreach (var entity in entityContext.Entities)
330✔
92
        {
93
            Options.Variables.Set(entity);
159✔
94

95
            var directory = Options.Data.Mapping.Directory ?? "Data\\Mapping";
159!
96
            var file = entity.MappingClass + ".cs";
159✔
97
            var path = Path.Combine(directory, file);
159✔
98

99
            if (File.Exists(path))
159✔
100
                _logger.LogInformation("Updating mapping class: {file}", file);
11✔
101
            else
102
                _logger.LogInformation("Creating mapping class: {file}", file);
148✔
103

104
            var template = new MappingClassTemplate(entity, Options);
159✔
105
            template.WriteCode(path);
159✔
106

107
            Options.Variables.Remove(entity);
159✔
108
        }
109
    }
6✔
110

111
    private void GenerateEntityClasses(EntityContext entityContext)
112
    {
113
        foreach (var entity in entityContext.Entities)
330✔
114
        {
115
            Options.Variables.Set(entity);
159✔
116

117
            var directory = Options.Data.Entity.Directory ?? "Data\\Entities";
159!
118
            var file = entity.EntityClass + ".cs";
159✔
119
            var path = Path.Combine(directory, file);
159✔
120

121
            if (File.Exists(path))
159✔
122
                _logger.LogInformation("Updating entity class: {file}", file);
11✔
123
            else
124
                _logger.LogInformation("Creating entity class: {file}", file);
148✔
125

126
            var template = new EntityClassTemplate(entity, Options);
159✔
127
            template.WriteCode(path);
159✔
128

129
            Options.Variables.Remove(entity);
159✔
130
        }
131
    }
6✔
132

133
    private void GenerateDataContext(EntityContext entityContext)
134
    {
135

136
        var directory = Options.Data.Context.Directory ?? "Data";
6!
137
        var file = entityContext.ContextClass + ".cs";
6✔
138
        var path = Path.Combine(directory, file);
6✔
139

140
        if (File.Exists(path))
6✔
141
            _logger.LogInformation("Updating data context class: {file}", file);
1✔
142
        else
143
            _logger.LogInformation("Creating data context class: {file}", file);
5✔
144

145
        var template = new DataContextTemplate(entityContext, Options);
6✔
146
        template.WriteCode(path);
6✔
147
    }
6✔
148

149

150
    private void GenerateModelClasses(EntityContext entityContext)
151
    {
152
        foreach (var entity in entityContext.Entities)
330✔
153
        {
154
            if (entity.Models.Count == 0)
159✔
155
                continue;
156

157
            Options.Variables.Set(entity);
137✔
158

159
            GenerateModelClasses(entity);
137✔
160
            GenerateValidatorClasses(entity);
137✔
161
            GenerateMapperClass(entity);
137✔
162

163
            Options.Variables.Remove(entity);
137✔
164
        }
165
    }
6✔
166

167

168
    private void GenerateModelClasses(Entity entity)
169
    {
170
        foreach (var model in entity.Models)
1,060✔
171
        {
172
            Options.Variables.Set(model);
393✔
173

174
            var directory = GetModelDirectory(model) ?? "Data\\Models";
393!
175
            var file = model.ModelClass + ".cs";
393✔
176
            var path = Path.Combine(directory, file);
393✔
177

178
            if (File.Exists(path))
393!
179
                _logger.LogInformation("Updating model class: {file}", file);
×
180
            else
181
                _logger.LogInformation("Creating model class: {file}", file);
393✔
182

183
            var template = new ModelClassTemplate(model, Options);
393✔
184
            template.WriteCode(path);
393✔
185

186
            Options.Variables.Remove(model);
393✔
187
        }
188

189
    }
137✔
190

191
    private string? GetModelDirectory(Model model)
192
    {
193
        if (model.ModelType == ModelType.Create)
393✔
194
        {
195
            return Options.Model.Create.Directory.HasValue()
128!
196
                ? Options.Model.Create.Directory
128✔
197
                : Options.Model.Shared.Directory;
128✔
198
        }
199

200
        if (model.ModelType == ModelType.Update)
265✔
201
        {
202
            return Options.Model.Update.Directory.HasValue()
128!
203
                ? Options.Model.Update.Directory
128✔
204
                : Options.Model.Shared.Directory;
128✔
205
        }
206

207
        return Options.Model.Read.Directory.HasValue()
137!
208
            ? Options.Model.Read.Directory
137✔
209
            : Options.Model.Shared.Directory;
137✔
210
    }
211

212

213
    private void GenerateValidatorClasses(Entity entity)
214
    {
215
        if (!Options.Model.Validator.Generate)
137!
UNCOV
216
            return;
×
217

218
        foreach (var model in entity.Models)
1,060✔
219
        {
220
            Options.Variables.Set(model);
393✔
221

222
            // don't validate read models
223
            if (model.ModelType == ModelType.Read)
393✔
224
                continue;
225

226
            var directory = Options.Model.Validator.Directory ?? "Data\\Validation";
256!
227
            var file = model.ValidatorClass + ".cs";
256✔
228
            var path = Path.Combine(directory, file);
256✔
229

230
            if (File.Exists(path))
256!
231
                _logger.LogInformation("Updating validation class: {file}", file);
×
232
            else
233
                _logger.LogInformation("Creating validation class: {file}", file);
256✔
234

235
            var template = new ValidatorClassTemplate(model, Options);
256✔
236
            template.WriteCode(path);
256✔
237

238
            Options.Variables.Remove(model);
256✔
239
        }
240
    }
137✔
241

242

243
    private void GenerateMapperClass(Entity entity)
244
    {
245
        if (!Options.Model.Mapper.Generate)
137!
UNCOV
246
            return;
×
247

248
        var directory = Options.Model.Mapper.Directory ?? "Data\\Mapper";
137!
249
        var file = entity.MapperClass + ".cs";
137✔
250
        var path = Path.Combine(directory, file);
137✔
251

252
        if (File.Exists(path))
137!
253
            _logger.LogInformation("Updating mapper class: {file}", file);
×
254
        else
255
            _logger.LogInformation("Creating mapper class: {file}", file);
137✔
256

257
        var template = new MapperClassTemplate(entity, Options);
137✔
258
        template.WriteCode(path);
137✔
259
    }
137✔
260

261

262
    private void GenerateScriptTemplates(EntityContext entityContext)
263
    {
264
        GenerateContextScriptTemplates(entityContext);
6✔
265
        GenerateEntityScriptTemplates(entityContext);
6✔
266
        GenerateModelScriptTemplates(entityContext);
6✔
267
    }
6✔
268

269
    private void GenerateModelScriptTemplates(EntityContext entityContext)
270
    {
271
        if (Options?.Script?.Model == null || Options.Script.Model.Count == 0)
6!
272
            return;
6✔
273

UNCOV
274
        foreach (var templateOption in Options.Script.Model)
×
275
        {
UNCOV
276
            if (!VerifyScriptTemplate(templateOption))
×
277
                continue;
278

279
            try
280
            {
UNCOV
281
                var template = new ModelScriptTemplate(_loggerFactory, Options, templateOption);
×
282

UNCOV
283
                foreach (var entity in entityContext.Entities)
×
284
                {
UNCOV
285
                    Options.Variables.Set(entity);
×
286

UNCOV
287
                    foreach (var model in entity.Models)
×
288
                    {
UNCOV
289
                        Options.Variables.Set(model);
×
290

UNCOV
291
                        template.RunScript(model);
×
292

UNCOV
293
                        Options.Variables.Remove(model);
×
294
                    }
295

296
                    Options.Variables.Remove(entity);
×
297
                }
UNCOV
298
            }
×
299
            catch (Exception ex)
×
300
            {
301
                _logger.LogError(ex, "Error Running Model Template: {message}", ex.Message);
×
302
            }
×
303
        }
304
    }
×
305

306
    private void GenerateEntityScriptTemplates(EntityContext entityContext)
307
    {
308
        if (Options?.Script?.Entity == null || Options.Script.Entity.Count == 0)
6!
309
            return;
6✔
310

UNCOV
311
        foreach (var templateOption in Options.Script.Entity)
×
312
        {
UNCOV
313
            if (!VerifyScriptTemplate(templateOption))
×
314
                continue;
315

316
            try
317
            {
UNCOV
318
                var template = new EntityScriptTemplate(_loggerFactory, Options, templateOption);
×
319

UNCOV
320
                foreach (var entity in entityContext.Entities)
×
321
                {
UNCOV
322
                    Options.Variables.Set(entity);
×
323

UNCOV
324
                    template.RunScript(entity);
×
325

UNCOV
326
                    Options.Variables.Remove(entity);
×
327
                }
UNCOV
328
            }
×
329
            catch (Exception ex)
×
330
            {
331
                _logger.LogError(ex, "Error Running Entity Template: {message}", ex.Message);
×
332
            }
×
333
        }
334
    }
×
335

336
    private void GenerateContextScriptTemplates(EntityContext entityContext)
337
    {
338
        if (Options?.Script?.Context == null || Options.Script.Context.Count! < 0)
6!
UNCOV
339
            return;
×
340

341
        foreach (var templateOption in Options.Script.Context)
12!
342
        {
UNCOV
343
            if (!VerifyScriptTemplate(templateOption))
×
344
                continue;
345

346
            try
347
            {
UNCOV
348
                var template = new ContextScriptTemplate(_loggerFactory, Options, templateOption);
×
UNCOV
349
                template.RunScript(entityContext);
×
UNCOV
350
            }
×
351
            catch (Exception ex)
×
352
            {
353
                _logger.LogError(ex, "Error Running Context Template: {message}", ex.Message);
×
354
            }
×
355
        }
356
    }
6✔
357

358
    private bool VerifyScriptTemplate(TemplateOptions templateOption)
359
    {
UNCOV
360
        var templatePath = templateOption.TemplatePath;
×
361

UNCOV
362
        if (File.Exists(templatePath))
×
363
            return true;
×
364

365
        _logger.LogWarning("Template '{template}' could not be found.", templatePath);
×
366
        return false;
×
367
    }
368

369

370
    private async Task<DatabaseModel> GetDatabaseModel(IDatabaseSchemaReader factory)
371
    {
372
        _logger.LogInformation("Loading database model ...");
6✔
373

374
        var database = Options.Database;
6✔
375

376
        var connectionString = ResolveConnectionString(database);
6✔
377
        if (string.IsNullOrEmpty(connectionString))
6!
UNCOV
378
            throw new InvalidOperationException("Could not find connection string.");
×
379

380
        var options = new SchemaReaderOptions
6✔
381
        {
6✔
382
            Schemas = Options.Database.Schemas,
6✔
383
            Tables = Options.Database.Tables
6✔
384
        };
6✔
385

386
        return await factory.ReadAsync(connectionString, options);
6✔
387
    }
6✔
388

389
    private static string? ResolveConnectionString(DatabaseOptions database)
390
    {
391
        if (database.ConnectionString.HasValue())
6!
392
            return database.ConnectionString;
6✔
393

UNCOV
394
        if (database.UserSecretsId.HasValue() && database.ConnectionName.HasValue())
×
395
        {
396
            var secretsStore = new SecretsStore(database.UserSecretsId);
×
397
            if (secretsStore.ContainsKey(database.ConnectionName))
×
UNCOV
398
                return secretsStore[database.ConnectionName];
×
399
        }
400

UNCOV
401
        throw new InvalidOperationException("Could not find connection string.");
×
402
    }
403

404

405
    private IDatabaseSchemaReader GetDatabaseProvider()
406
    {
407
        var provider = Options.Database.Provider;
6✔
408

409
        _logger.LogDebug("Creating database model factory for: {provider}", provider);
6✔
410

411
        return provider switch
6!
412
        {
6✔
413
            DatabaseProviders.SqlServer => new SchemaSaurus.SqlServer.SqlServerSchemaReader(),
3✔
414
            DatabaseProviders.PostgreSQL => new SchemaSaurus.PostgreSql.PostgreSqlSchemaReader(),
1✔
415
            DatabaseProviders.MySQL => new SchemaSaurus.MySql.MySqlSchemaReader(),
1✔
416
            DatabaseProviders.Sqlite => new SchemaSaurus.Sqlite.SqliteSchemaReader(),
1✔
UNCOV
417
            DatabaseProviders.Oracle => new SchemaSaurus.Oracle.OracleSchemaReader(),
×
UNCOV
418
            _ => throw new NotSupportedException($"The specified provider '{provider}' is not supported."),
×
419
        };
6✔
420
    }
421
}
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