• 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

37.43
/src/EntityFrameworkCore.Generator.Core/CodeGenerator.cs
1
using System;
2
using System.IO;
3
using EntityFrameworkCore.Generator.Extensions;
4
using EntityFrameworkCore.Generator.Metadata.Generation;
5
using EntityFrameworkCore.Generator.Options;
6
using EntityFrameworkCore.Generator.Parsing;
7
using EntityFrameworkCore.Generator.Scripts;
8
using EntityFrameworkCore.Generator.Templates;
9
using Microsoft.EntityFrameworkCore.Design;
10
using Microsoft.EntityFrameworkCore.Scaffolding;
11
using Microsoft.EntityFrameworkCore.Scaffolding.Metadata;
12
using Microsoft.EntityFrameworkCore.Storage;
13
using Microsoft.Extensions.DependencyInjection;
14
using Microsoft.Extensions.Logging;
15

16
namespace EntityFrameworkCore.Generator;
17

18
public class CodeGenerator : ICodeGenerator
19
{
20
    private readonly ILoggerFactory _loggerFactory;
21
    private readonly ILogger _logger;
22
    private readonly ModelGenerator _modelGenerator;
23
    private readonly SourceSynchronizer _synchronizer;
24

25
    public CodeGenerator(ILoggerFactory loggerFactory)
3✔
26
    {
27
        _loggerFactory = loggerFactory;
3✔
28
        _logger = loggerFactory.CreateLogger<CodeGenerator>();
3✔
29
        _modelGenerator = new ModelGenerator(loggerFactory);
3✔
30
        _synchronizer = new SourceSynchronizer(loggerFactory);
3✔
31
    }
3✔
32

33
    public GeneratorOptions Options { get; set; } = null!;
306✔
34

35
    public bool Generate(GeneratorOptions options)
36
    {
37
        Options = options ?? throw new ArgumentNullException(nameof(options));
3!
38

39
        var databaseProviders = GetDatabaseProviders();
3✔
40
        var databaseModel = GetDatabaseModel(databaseProviders.factory);
3✔
41

42
        if (databaseModel == null)
3!
43
            throw new InvalidOperationException("Failed to create database model");
×
44

45
        _logger.LogInformation("Loaded database model for: {databaseName}", databaseModel.DatabaseName);
3✔
46

47
        var context = _modelGenerator.Generate(Options, databaseModel, databaseProviders.mapping);
3✔
48

49
        _synchronizer.UpdateFromSource(context, options);
3✔
50

51
        GenerateFiles(context);
3✔
52

53
        return true;
3✔
54
    }
55

56
    private void GenerateFiles(EntityContext entityContext)
57
    {
58
        GenerateDataContext(entityContext);
3✔
59
        GenerateEntityClasses(entityContext);
3✔
60
        GenerateMappingClasses(entityContext);
3✔
61

62
        if (Options.Data.Query.Generate)
3!
63
            GenerateQueryExtensions(entityContext);
×
64

65
        GenerateModelClasses(entityContext);
3✔
66

67
        GenerateScriptTemplates(entityContext);
3✔
68
    }
3✔
69

70
    private void GenerateQueryExtensions(EntityContext entityContext)
71
    {
72
        foreach (var entity in entityContext.Entities)
×
73
        {
74
            Options.Variables.Set(entity);
×
75

NEW
76
            var directory = Options.Data.Query.Directory ?? "Data\\Queries";
×
77
            var file = entity.EntityClass + "Extensions.cs";
×
78
            var path = Path.Combine(directory, file);
×
79

NEW
80
            if (File.Exists(path))
×
NEW
81
                _logger.LogInformation("Updating query extensions class: {file}", file);
×
82
            else
NEW
83
                _logger.LogInformation("Creating query extensions class: {file}", file);
×
84

85
            var template = new QueryExtensionTemplate(entity, Options);
×
86
            template.WriteCode(path);
×
87

88
            Options.Variables.Remove(entity);
×
89
        }
90
    }
×
91

92
    private void GenerateMappingClasses(EntityContext entityContext)
93
    {
94
        foreach (var entity in entityContext.Entities)
72✔
95
        {
96
            Options.Variables.Set(entity);
33✔
97

98
            var directory = Options.Data.Mapping.Directory ?? "Data\\Mapping";
33!
99
            var file = entity.MappingClass + ".cs";
33✔
100
            var path = Path.Combine(directory, file);
33✔
101

102
            if (File.Exists(path))
33✔
103
                _logger.LogInformation("Updating mapping class: {file}", file);
22✔
104
            else
105
                _logger.LogInformation("Creating mapping class: {file}", file);
11✔
106

107
            var template = new MappingClassTemplate(entity, Options);
33✔
108
            template.WriteCode(path);
33✔
109

110
            Options.Variables.Remove(entity);
33✔
111
        }
112
    }
3✔
113

114
    private void GenerateEntityClasses(EntityContext entityContext)
115
    {
116
        foreach (var entity in entityContext.Entities)
72✔
117
        {
118
            Options.Variables.Set(entity);
33✔
119

120
            var directory = Options.Data.Entity.Directory ?? "Data\\Entities";
33!
121
            var file = entity.EntityClass + ".cs";
33✔
122
            var path = Path.Combine(directory, file);
33✔
123

124
            if (File.Exists(path))
33✔
125
                _logger.LogInformation("Updating entity class: {file}", file);
22✔
126
            else
127
                _logger.LogInformation("Creating entity class: {file}", file);
11✔
128

129
            var template = new EntityClassTemplate(entity, Options);
33✔
130
            template.WriteCode(path);
33✔
131

132
            Options.Variables.Remove(entity);
33✔
133
        }
134
    }
3✔
135

136
    private void GenerateDataContext(EntityContext entityContext)
137
    {
138

139
        var directory = Options.Data.Context.Directory ?? "Data";
3!
140
        var file = entityContext.ContextClass + ".cs";
3✔
141
        var path = Path.Combine(directory, file);
3✔
142

143
        if (File.Exists(path))
3✔
144
            _logger.LogInformation("Updating data context class: {file}", file);
2✔
145
        else
146
            _logger.LogInformation("Creating data context class: {file}", file);
1✔
147

148
        var template = new DataContextTemplate(entityContext, Options);
3✔
149
        template.WriteCode(path);
3✔
150
    }
3✔
151

152

153
    private void GenerateModelClasses(EntityContext entityContext)
154
    {
155
        foreach (var entity in entityContext.Entities)
72✔
156
        {
157
            if (entity.Models.Count == 0)
33!
158
                continue;
159

160
            Options.Variables.Set(entity);
×
161

162
            GenerateModelClasses(entity);
×
163
            GenerateValidatorClasses(entity);
×
164
            GenerateMapperClass(entity);
×
165

166
            Options.Variables.Remove(entity);
×
167
        }
168
    }
3✔
169

170

171
    private void GenerateModelClasses(Entity entity)
172
    {
173
        foreach (var model in entity.Models)
×
174
        {
175
            Options.Variables.Set(model);
×
176

NEW
177
            var directory = GetModelDirectory(model) ?? "Data\\Models";
×
178
            var file = model.ModelClass + ".cs";
×
179
            var path = Path.Combine(directory, file);
×
180

NEW
181
            if (File.Exists(path))
×
NEW
182
                _logger.LogInformation("Updating model class: {file}", file);
×
183
            else
NEW
184
                _logger.LogInformation("Creating model class: {file}", file);
×
185

186
            var template = new ModelClassTemplate(model, Options);
×
187
            template.WriteCode(path);
×
188

189
            Options.Variables.Remove(model);
×
190
        }
191

192
    }
×
193

194
    private string? GetModelDirectory(Model model)
195
    {
196
        if (model.ModelType == ModelType.Create)
×
197
        {
198
            return Options.Model.Create.Directory.HasValue()
×
199
                ? Options.Model.Create.Directory
×
200
                : Options.Model.Shared.Directory;
×
201
        }
202

203
        if (model.ModelType == ModelType.Update)
×
204
        {
205
            return Options.Model.Update.Directory.HasValue()
×
206
                ? Options.Model.Update.Directory
×
207
                : Options.Model.Shared.Directory;
×
208
        }
209

210
        return Options.Model.Read.Directory.HasValue()
×
211
            ? Options.Model.Read.Directory
×
212
            : Options.Model.Shared.Directory;
×
213
    }
214

215

216
    private void GenerateValidatorClasses(Entity entity)
217
    {
218
        if (!Options.Model.Validator.Generate)
×
219
            return;
×
220

221
        foreach (var model in entity.Models)
×
222
        {
223
            Options.Variables.Set(model);
×
224

225
            // don't validate read models
226
            if (model.ModelType == ModelType.Read)
×
227
                continue;
228

NEW
229
            var directory = Options.Model.Validator.Directory ?? "Data\\Validation";
×
230
            var file = model.ValidatorClass + ".cs";
×
231
            var path = Path.Combine(directory, file);
×
232

NEW
233
            if (File.Exists(path))
×
NEW
234
                _logger.LogInformation("Updating validation class: {file}", file);
×
235
            else
NEW
236
                _logger.LogInformation("Creating validation class: {file}", file);
×
237

238
            var template = new ValidatorClassTemplate(model, Options);
×
239
            template.WriteCode(path);
×
240

241
            Options.Variables.Remove(model);
×
242
        }
243
    }
×
244

245

246
    private void GenerateMapperClass(Entity entity)
247
    {
248
        if (!Options.Model.Mapper.Generate)
×
249
            return;
×
250

NEW
251
        var directory = Options.Model.Mapper.Directory ?? "Data\\Mapper";
×
252
        var file = entity.MapperClass + ".cs";
×
253
        var path = Path.Combine(directory, file);
×
254

NEW
255
        if (File.Exists(path))
×
NEW
256
            _logger.LogInformation("Updating mapper class: {file}", file);
×
257
        else
NEW
258
            _logger.LogInformation("Creating mapper class: {file}", file);
×
259

260
        var template = new MapperClassTemplate(entity, Options);
×
261
        template.WriteCode(path);
×
262
    }
×
263

264

265
    private void GenerateScriptTemplates(EntityContext entityContext)
266
    {
267
        GenerateContextScriptTemplates(entityContext);
3✔
268
        GenerateEntityScriptTemplates(entityContext);
3✔
269
        GenerateModelScriptTemplates(entityContext);
3✔
270
    }
3✔
271

272
    private void GenerateModelScriptTemplates(EntityContext entityContext)
273
    {
274
        if (Options?.Script?.Model == null || Options.Script.Model.Count == 0)
3!
275
            return;
3✔
276

277
        foreach (var templateOption in Options.Script.Model)
×
278
        {
279
            if (!VerifyScriptTemplate(templateOption))
×
280
                continue;
281

282
            try
283
            {
284
                var template = new ModelScriptTemplate(_loggerFactory, Options, templateOption);
×
285

286
                foreach (var entity in entityContext.Entities)
×
287
                {
288
                    Options.Variables.Set(entity);
×
289

290
                    foreach (var model in entity.Models)
×
291
                    {
292
                        Options.Variables.Set(model);
×
293

294
                        template.RunScript(model);
×
295

296
                        Options.Variables.Remove(model);
×
297
                    }
298

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

309
    private void GenerateEntityScriptTemplates(EntityContext entityContext)
310
    {
311
        if (Options?.Script?.Entity == null || Options.Script.Entity.Count == 0)
3!
312
            return;
3✔
313

314
        foreach (var templateOption in Options.Script.Entity)
×
315
        {
316
            if (!VerifyScriptTemplate(templateOption))
×
317
                continue;
318

319
            try
320
            {
321
                var template = new EntityScriptTemplate(_loggerFactory, Options, templateOption);
×
322

323
                foreach (var entity in entityContext.Entities)
×
324
                {
325
                    Options.Variables.Set(entity);
×
326

327
                    template.RunScript(entity);
×
328

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

339
    private void GenerateContextScriptTemplates(EntityContext entityContext)
340
    {
341
        if (Options?.Script?.Context == null || Options.Script.Context.Count !< 0)
3!
342
            return;
×
343

344
        foreach (var templateOption in Options.Script.Context)
6!
345
        {
346
            if (!VerifyScriptTemplate(templateOption))
×
347
                continue;
348

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

361
    private bool VerifyScriptTemplate(TemplateOptions templateOption)
362
    {
363
        var templatePath = templateOption.TemplatePath;
×
364

365
        if (File.Exists(templatePath))
×
366
            return true;
×
367

368
        _logger.LogWarning("Template '{template}' could not be found.", templatePath);
×
369
        return false;
×
370
    }
371

372

373
    private DatabaseModel GetDatabaseModel(IDatabaseModelFactory factory)
374
    {
375
        _logger.LogInformation("Loading database model ...");
3✔
376

377
        var database = Options.Database;
3✔
378

379
        var connectionString = ResolveConnectionString(database);
3✔
380
        if (string.IsNullOrEmpty(connectionString))
3!
NEW
381
            throw new InvalidOperationException("Could not find connection string.");
×
382

383
        var options = new DatabaseModelFactoryOptions(database.Tables, database.Schemas);
3✔
384

385
        return factory.Create(connectionString, options);
3✔
386
    }
387

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

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

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

403

404
    private (IDatabaseModelFactory factory, IRelationalTypeMappingSource mapping) GetDatabaseProviders()
405
    {
406
        var provider = Options.Database.Provider;
3✔
407

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

410
        // start a new service container to create the database model factory
411
        var services = new ServiceCollection()
3!
412
            .AddSingleton(_loggerFactory)
3✔
413
            .AddEntityFrameworkDesignTimeServices();
3✔
414

415
        switch (provider)
416
        {
417
            case DatabaseProviders.SqlServer:
418
                ConfigureSqlServerServices(services);
3✔
419
                break;
3✔
420
            case DatabaseProviders.PostgreSQL:
421
                ConfigurePostgresServices(services);
×
422
                break;
×
423
            case DatabaseProviders.MySQL:
424
                ConfigureMySqlServices(services);
×
425
                break;
×
426
            case DatabaseProviders.Sqlite:
427
                ConfigureSqliteServices(services);
×
428
                break;
×
429
            case DatabaseProviders.Oracle:
430
                ConfigureOracleServices(services);
×
431
                break;
×
432
            default:
433
                throw new NotSupportedException($"The specified provider '{provider}' is not supported.");
×
434
        }
435

436
        var serviceProvider = services
3✔
437
            .BuildServiceProvider();
3✔
438

439
        var databaseModelFactory = serviceProvider
3✔
440
            .GetRequiredService<IDatabaseModelFactory>();
3✔
441

442
        var typeMappingSource = serviceProvider
3✔
443
            .GetRequiredService<IRelationalTypeMappingSource>();
3✔
444

445
        return (databaseModelFactory, typeMappingSource);
3✔
446
    }
447

448

449
    private static void ConfigureMySqlServices(IServiceCollection services)
450
    {
451
        var designTimeServices = new Pomelo.EntityFrameworkCore.MySql.Design.Internal.MySqlDesignTimeServices();
×
452
        designTimeServices.ConfigureDesignTimeServices(services);
×
453
            services.AddEntityFrameworkMySqlNetTopologySuite();
×
454
    }
×
455

456
    private static void ConfigurePostgresServices(IServiceCollection services)
457
    {
458
        var designTimeServices = new Npgsql.EntityFrameworkCore.PostgreSQL.Design.Internal.NpgsqlDesignTimeServices();
×
459
        designTimeServices.ConfigureDesignTimeServices(services);
×
460
            services.AddEntityFrameworkNpgsqlNetTopologySuite();
×
461
    }
×
462

463
    private static void ConfigureSqlServerServices(IServiceCollection services)
464
    {
465
        var designTimeServices = new Microsoft.EntityFrameworkCore.SqlServer.Design.Internal.SqlServerDesignTimeServices();
3✔
466
        designTimeServices.ConfigureDesignTimeServices(services);
3✔
467
            services.AddEntityFrameworkSqlServerNetTopologySuite();
3✔
468
    }
3✔
469

470
    private static void ConfigureSqliteServices(IServiceCollection services)
471
    {
472
        var designTimeServices = new Microsoft.EntityFrameworkCore.Sqlite.Design.Internal.SqliteDesignTimeServices();
×
473
        designTimeServices.ConfigureDesignTimeServices(services);
×
474
            services.AddEntityFrameworkSqliteNetTopologySuite();
×
475
    }
×
476

477
    private static void ConfigureOracleServices(IServiceCollection services)
478
    {
479
        var designTimeServices = new Oracle.EntityFrameworkCore.Design.Internal.OracleDesignTimeServices();
×
480
        designTimeServices.ConfigureDesignTimeServices(services);
×
481
    }
×
482
}
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