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

loresoft / EntityFrameworkCore.Generator / 8395321437

22 Mar 2024 07:13PM UTC coverage: 55.317% (+10.1%) from 45.252%
8395321437

push

github

pwelter34
Update EntityFrameworkCore.Generator.Core.csproj

554 of 1079 branches covered (51.34%)

Branch coverage included in aggregate %.

1563 of 2748 relevant lines covered (56.88%)

51.04 hits per line

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

37.8
/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)
26
    {
27
        _loggerFactory = loggerFactory;
2✔
28
        _logger = loggerFactory.CreateLogger<CodeGenerator>();
2✔
29
        _modelGenerator = new ModelGenerator(loggerFactory);
2✔
30
        _synchronizer = new SourceSynchronizer(loggerFactory);
2✔
31
    }
2✔
32

33
    public GeneratorOptions Options { get; set; }
34

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

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

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

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

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

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

51
        GenerateFiles(context);
2✔
52

53
        return true;
2✔
54
    }
55

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

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

65
        GenerateModelClasses(entityContext);
2✔
66

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

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

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

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

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

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

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

97
            var directory = Options.Data.Mapping.Directory;
22✔
98
            var file = entity.MappingClass + ".cs";
22✔
99
            var path = Path.Combine(directory, file);
22✔
100

101
            _logger.LogInformation(File.Exists(path)
22✔
102
                ? "Updating mapping class: {file}"
22✔
103
                : "Creating mapping class: {file}", file);
22✔
104

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

108
            Options.Variables.Remove(entity);
22✔
109
        }
110
    }
2✔
111

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

118
            var directory = Options.Data.Entity.Directory;
22✔
119
            var file = entity.EntityClass + ".cs";
22✔
120
            var path = Path.Combine(directory, file);
22✔
121

122
            _logger.LogInformation(File.Exists(path)
22✔
123
                ? "Updating entity class: {file}"
22✔
124
                : "Creating entity class: {file}", file);
22✔
125

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

129
            Options.Variables.Remove(entity);
22✔
130
        }
131
    }
2✔
132

133
    private void GenerateDataContext(EntityContext entityContext)
134
    {
135

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

140
        _logger.LogInformation(File.Exists(path)
2✔
141
            ? "Updating data context class: {file}"
2✔
142
            : "Creating data context class: {file}", file);
2✔
143

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

148

149
    private void GenerateModelClasses(EntityContext entityContext)
150
    {
151
        foreach (var entity in entityContext.Entities)
48✔
152
        {
153
            if (entity.Models.Count <= 0)
22!
154
                continue;
155

156
            Options.Variables.Set(entity);
×
157

158
            GenerateModelClasses(entity);
×
159
            GenerateValidatorClasses(entity);
×
160
            GenerateMapperClass(entity);
×
161

162
            Options.Variables.Remove(entity);
×
163
        }
164
    }
2✔
165

166

167
    private void GenerateModelClasses(Entity entity)
168
    {
169
        foreach (var model in entity.Models)
×
170
        {
171
            Options.Variables.Set(model);
×
172

173
            var directory = GetModelDirectory(model);
×
174
            var file = model.ModelClass + ".cs";
×
175
            var path = Path.Combine(directory, file);
×
176

177
            _logger.LogInformation(File.Exists(path)
×
178
                ? "Updating model class: {file}"
×
179
                : "Creating model class: {file}", file);
×
180

181

182
            var template = new ModelClassTemplate(model, Options);
×
183
            template.WriteCode(path);
×
184

185
            Options.Variables.Remove(model);
×
186
        }
187

188
    }
×
189

190
    private string GetModelDirectory(Model model)
191
    {
192
        if (model.ModelType == ModelType.Create)
×
193
            return Options.Model.Create.Directory.HasValue()
×
194
                ? Options.Model.Create.Directory
×
195
                : Options.Model.Shared.Directory;
×
196

197
        if (model.ModelType == ModelType.Update)
×
198
            return Options.Model.Update.Directory.HasValue()
×
199
                ? Options.Model.Update.Directory
×
200
                : Options.Model.Shared.Directory;
×
201

202
        return Options.Model.Read.Directory.HasValue()
×
203
            ? Options.Model.Read.Directory
×
204
            : Options.Model.Shared.Directory;
×
205
    }
206

207

208
    private void GenerateValidatorClasses(Entity entity)
209
    {
210
        if (!Options.Model.Validator.Generate)
×
211
            return;
×
212

213
        foreach (var model in entity.Models)
×
214
        {
215
            Options.Variables.Set(model);
×
216

217
            // don't validate read models
218
            if (model.ModelType == ModelType.Read)
×
219
                continue;
220

221
            var directory = Options.Model.Validator.Directory;
×
222
            var file = model.ValidatorClass + ".cs";
×
223
            var path = Path.Combine(directory, file);
×
224

225
            _logger.LogInformation(File.Exists(path)
×
226
                ? "Updating validation class: {file}"
×
227
                : "Creating validation class: {file}", file);
×
228

229
            var template = new ValidatorClassTemplate(model, Options);
×
230
            template.WriteCode(path);
×
231

232
            Options.Variables.Remove(model);
×
233
        }
234
    }
×
235

236

237
    private void GenerateMapperClass(Entity entity)
238
    {
239
        if (!Options.Model.Mapper.Generate)
×
240
            return;
×
241

242
        var directory = Options.Model.Mapper.Directory;
×
243
        var file = entity.MapperClass + ".cs";
×
244
        var path = Path.Combine(directory, file);
×
245

246
        _logger.LogInformation(File.Exists(path)
×
247
            ? "Updating object mapper class: {file}"
×
248
            : "Creating object mapper class: {file}", file);
×
249

250
        var template = new MapperClassTemplate(entity, Options);
×
251
        template.WriteCode(path);
×
252
    }
×
253

254

255
    private void GenerateScriptTemplates(EntityContext entityContext)
256
    {
257
        GenerateContextScriptTemplates(entityContext);
2✔
258
        GenerateEntityScriptTemplates(entityContext);
2✔
259
        GenerateModelScriptTemplates(entityContext);
2✔
260
    }
2✔
261

262
    private void GenerateModelScriptTemplates(EntityContext entityContext)
263
    {
264
        if (Options?.Script?.Model == null || Options.Script.Model.Count == 0)
2!
265
            return;
2✔
266

267
        foreach (var templateOption in Options.Script.Model)
×
268
        {
269
            if (!VerifyScriptTemplate(templateOption))
×
270
                continue;
271

272
            try
273
            {
274
                var template = new ModelScriptTemplate(_loggerFactory, Options, templateOption);
×
275

276
                foreach (var entity in entityContext.Entities)
×
277
                {
278
                    Options.Variables.Set(entity);
×
279

280
                    foreach (var model in entity.Models)
×
281
                    {
282
                        Options.Variables.Set(model);
×
283

284
                        template.RunScript(model);
×
285

286
                        Options.Variables.Remove(model);
×
287
                    }
288

289
                    Options.Variables.Remove(entity);
×
290
                }
291
            }
×
292
            catch (Exception ex)
×
293
            {
294
                _logger.LogError(ex, "Error Running Model Template: {message}", ex.Message);
×
295
            }
×
296
        }
297
    }
×
298

299
    private void GenerateEntityScriptTemplates(EntityContext entityContext)
300
    {
301
        if (Options?.Script?.Entity == null || Options.Script.Entity.Count == 0)
2!
302
            return;
2✔
303

304
        foreach (var templateOption in Options.Script.Entity)
×
305
        {
306
            if (!VerifyScriptTemplate(templateOption))
×
307
                continue;
308

309
            try
310
            {
311
                var template = new EntityScriptTemplate(_loggerFactory, Options, templateOption);
×
312

313
                foreach (var entity in entityContext.Entities)
×
314
                {
315
                    Options.Variables.Set(entity);
×
316

317
                    template.RunScript(entity);
×
318

319
                    Options.Variables.Remove(entity);
×
320
                }
321
            }
×
322
            catch (Exception ex)
×
323
            {
324
                _logger.LogError(ex, "Error Running Entity Template: {message}", ex.Message);
×
325
            }
×
326
        }
327
    }
×
328

329
    private void GenerateContextScriptTemplates(EntityContext entityContext)
330
    {
331
        if (Options?.Script?.Context == null || Options.Script.Context.Count !< 0)
2!
332
            return;
×
333

334
        foreach (var templateOption in Options.Script.Context)
4!
335
        {
336
            if (!VerifyScriptTemplate(templateOption))
×
337
                continue;
338

339
            try
340
            {
341
                var template = new ContextScriptTemplate(_loggerFactory, Options, templateOption);
×
342
                template.RunScript(entityContext);
×
343
            }
×
344
            catch (Exception ex)
×
345
            {
346
                _logger.LogError(ex, "Error Running Context Template: {message}", ex.Message);
×
347
            }
×
348
        }
349
    }
2✔
350

351
    private bool VerifyScriptTemplate(TemplateOptions templateOption)
352
    {
353
        var templatePath = templateOption.TemplatePath;
×
354

355
        if (File.Exists(templatePath))
×
356
            return true;
×
357

358
        _logger.LogWarning("Template '{template}' could not be found.", templatePath);
×
359
        return false;
×
360
    }
361

362

363
    private DatabaseModel GetDatabaseModel(IDatabaseModelFactory factory)
364
    {
365
        _logger.LogInformation("Loading database model ...");
2✔
366

367
        var database = Options.Database;
2✔
368
        var connectionString = ResolveConnectionString(database);
2✔
369
        var options = new DatabaseModelFactoryOptions(database.Tables, database.Schemas);
2✔
370

371
        return factory.Create(connectionString, options);
2✔
372
    }
373

374
    private string ResolveConnectionString(DatabaseOptions database)
375
    {
376
        if (database.ConnectionString.HasValue())
2!
377
            return database.ConnectionString;
2✔
378

379
        if (database.UserSecretsId.HasValue())
×
380
        {
381
            var secretsStore = new SecretsStore(database.UserSecretsId);
×
382
            if (secretsStore.ContainsKey(database.ConnectionName))
×
383
                return secretsStore[database.ConnectionName];
×
384
        }
385

386
        throw new InvalidOperationException("Could not find connection string.");
×
387
    }
388

389

390
    private (IDatabaseModelFactory factory, IRelationalTypeMappingSource mapping) GetDatabaseProviders()
391
    {
392
        var provider = Options.Database.Provider;
2✔
393

394
        _logger.LogDebug("Creating database model factory for: {provider}", provider);
2✔
395

396
        // start a new service container to create the database model factory
397
        var services = new ServiceCollection()
2!
398
            .AddSingleton(_loggerFactory)
2✔
399
            .AddEntityFrameworkDesignTimeServices();
2✔
400

401
        switch (provider)
402
        {
403
            case DatabaseProviders.SqlServer:
404
                ConfigureSqlServerServices(services);
2✔
405
                break;
2✔
406
            case DatabaseProviders.PostgreSQL:
407
                ConfigurePostgresServices(services);
×
408
                break;
×
409
            case DatabaseProviders.MySQL:
410
                ConfigureMySqlServices(services);
×
411
                break;
×
412
            case DatabaseProviders.Sqlite:
413
                ConfigureSqliteServices(services);
×
414
                break;
×
415
            case DatabaseProviders.Oracle:
416
                ConfigureOracleServices(services);
×
417
                break;
×
418
            default:
419
                throw new NotSupportedException($"The specified provider '{provider}' is not supported.");
×
420
        }
421

422
        var serviceProvider = services
2✔
423
            .BuildServiceProvider();
2✔
424

425
        var databaseModelFactory = serviceProvider
2✔
426
            .GetRequiredService<IDatabaseModelFactory>();
2✔
427

428
        var typeMappingSource = serviceProvider
2✔
429
            .GetRequiredService<IRelationalTypeMappingSource>();
2✔
430

431
        return (databaseModelFactory, typeMappingSource);
2✔
432
    }
433

434

435
    private void ConfigureMySqlServices(IServiceCollection services)
436
    {
437
        var designTimeServices = new Pomelo.EntityFrameworkCore.MySql.Design.Internal.MySqlDesignTimeServices();
×
438
        designTimeServices.ConfigureDesignTimeServices(services);
×
439
            services.AddEntityFrameworkMySqlNetTopologySuite();
×
440
    }
×
441

442
    private void ConfigurePostgresServices(IServiceCollection services)
443
    {
444
        var designTimeServices = new Npgsql.EntityFrameworkCore.PostgreSQL.Design.Internal.NpgsqlDesignTimeServices();
×
445
        designTimeServices.ConfigureDesignTimeServices(services);
×
446
            services.AddEntityFrameworkNpgsqlNetTopologySuite();
×
447
    }
×
448

449
    private void ConfigureSqlServerServices(IServiceCollection services)
450
    {
451
        var designTimeServices = new Microsoft.EntityFrameworkCore.SqlServer.Design.Internal.SqlServerDesignTimeServices();
2✔
452
        designTimeServices.ConfigureDesignTimeServices(services);
2✔
453
            services.AddEntityFrameworkSqlServerNetTopologySuite();
2✔
454
    }
2✔
455

456
    private void ConfigureSqliteServices(IServiceCollection services)
457
    {
458
        var designTimeServices = new Microsoft.EntityFrameworkCore.Sqlite.Design.Internal.SqliteDesignTimeServices();
×
459
        designTimeServices.ConfigureDesignTimeServices(services);
×
460
            services.AddEntityFrameworkSqliteNetTopologySuite();
×
461
    }
×
462

463
    private void ConfigureOracleServices(IServiceCollection services)
464
    {
465
        var designTimeServices = new Oracle.EntityFrameworkCore.Design.Internal.OracleDesignTimeServices();
×
466
        designTimeServices.ConfigureDesignTimeServices(services);
×
467
    }
×
468
}
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