• 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

78.51
/src/EntityFrameworkCore.Generator.Core/ModelGenerator.cs
1
using System.Data;
2
using System.Text;
3
using System.Text.RegularExpressions;
4

5
using EntityFrameworkCore.Generator.Extensions;
6
using EntityFrameworkCore.Generator.Metadata.Generation;
7
using EntityFrameworkCore.Generator.Options;
8

9
using Humanizer;
10

11
using Microsoft.Extensions.Logging;
12

13
using SchemaSaurus.Metadata;
14

15
using Model = EntityFrameworkCore.Generator.Metadata.Generation.Model;
16
using Property = EntityFrameworkCore.Generator.Metadata.Generation.Property;
17

18
namespace EntityFrameworkCore.Generator;
19

20
public partial class ModelGenerator
21
{
22
    private readonly UniqueNamer _namer;
23
    private readonly ILogger _logger;
24
    private GeneratorOptions _options = null!;
25

26
    public ModelGenerator(ILoggerFactory logger)
16✔
27
    {
28
        _logger = logger.CreateLogger<ModelGenerator>();
16✔
29
        _namer = new UniqueNamer();
16✔
30
    }
16✔
31

32
    public EntityContext Generate(GeneratorOptions options, DatabaseModel databaseModel)
33
    {
34
        ArgumentNullException.ThrowIfNull(options);
16✔
35
        ArgumentNullException.ThrowIfNull(databaseModel);
16✔
36

37
        _logger.LogInformation("Building code generation model from database: {databaseName}", databaseModel.DatabaseName);
16✔
38

39
        _options = options;
16✔
40

41
        var entityContext = new EntityContext();
16✔
42
        entityContext.DatabaseName = databaseModel.DatabaseName;
16✔
43

44
        // update database variables
45
        _options.Database.Name = ToLegalName(databaseModel.DatabaseName);
16✔
46

47

48
        string contextClass = _options.Data.Context.Name ?? "DataContext";
16!
49
        contextClass = _namer.UniqueClassName(contextClass);
16✔
50

51
        string contextNamespace = _options.Data.Context.Namespace ?? "Data";
16!
52
        string contextBaseClass = _options.Data.Context.BaseClass ?? "DbContext";
16!
53

54
        entityContext.ContextClass = contextClass;
16✔
55
        entityContext.ContextNamespace = contextNamespace;
16✔
56
        entityContext.ContextBaseClass = contextBaseClass;
16✔
57

58
        foreach (var table in databaseModel.Tables)
364✔
59
        {
60
            if (IsIgnored(table, _options.Database.Exclude.Tables))
166✔
61
            {
62
                _logger.LogDebug("  Skipping Table : {schema}.{name}", table.Schema, table.Name);
2✔
63
                continue;
2✔
64
            }
65

66
            _logger.LogDebug("  Processing Table : {schema}.{name}", table.Schema, table.Name);
164✔
67

68
            _options.Variables.Set(VariableConstants.TableSchema, ToLegalName(table.Schema));
164✔
69
            _options.Variables.Set(VariableConstants.TableName, ToLegalName(table.Name));
164✔
70

71
            var entity = GetEntity(entityContext, table);
164✔
72
            GetModels(entity);
164✔
73
        }
74

75
        foreach (var view in databaseModel.Views)
44✔
76
        {
77
            if (IsIgnored(view, _options.Database.Exclude.Tables))
6!
78
            {
UNCOV
79
                _logger.LogDebug("  Skipping View : {schema}.{name}", view.Schema, view.Name);
×
UNCOV
80
                continue;
×
81
            }
82

83
            _logger.LogDebug("  Processing View : {schema}.{name}", view.Schema, view.Name);
6✔
84

85
            _options.Variables.Set(VariableConstants.TableSchema, ToLegalName(view.Schema));
6✔
86
            _options.Variables.Set(VariableConstants.TableName, ToLegalName(view.Name));
6✔
87

88
            var entity = GetEntity(entityContext, view);
6✔
89
            GetModels(entity);
6✔
90
        }
91

92
        _options.Variables.Remove(VariableConstants.TableName);
16✔
93
        _options.Variables.Remove(VariableConstants.TableSchema);
16✔
94

95
        return entityContext;
16✔
96
    }
97

98

99
    private Entity GetEntity(EntityContext entityContext, RelationBase relationSchema, bool processRelationships = true, bool processMethods = true)
100
    {
101
        var entity = entityContext.Entities.ByTable(relationSchema.Name, relationSchema.Schema)
247✔
102
            ?? CreateEntity(entityContext, relationSchema);
247✔
103

104
        if (!entity.Properties.IsProcessed)
247✔
105
            CreateProperties(entity, relationSchema);
170✔
106

107
        if (relationSchema is not Table tableSchema)
247✔
108
            return entity;
6✔
109

110
        if (processRelationships && !entity.Relationships.IsProcessed)
241!
111
            CreateRelationships(entityContext, entity, tableSchema);
164✔
112

113
        if (processMethods && !entity.Methods.IsProcessed)
241!
114
            CreateMethods(entity, tableSchema);
164✔
115

116
        entity.IsProcessed = true;
241✔
117
        return entity;
241✔
118
    }
119

120
    private Entity CreateEntity(EntityContext entityContext, RelationBase relationSchema)
121
    {
122
        var entity = new Entity
170✔
123
        {
170✔
124
            Context = entityContext,
170✔
125
            TableName = relationSchema.Name,
170✔
126
            TableSchema = relationSchema.Schema
170✔
127
        };
170✔
128

129
        // add to context
130
        entityContext.Entities.Add(entity);
170✔
131

132
        var entityClass = _options.Data.Entity.Name;
170✔
133
        if (entityClass.IsNullOrEmpty())
170!
134
            entityClass = ToClassName(relationSchema);
170✔
135

136
        entityClass = _namer.UniqueClassName(entityClass);
170✔
137

138
        var entityNamespace = _options.Data.Entity.Namespace ?? "Data.Entities";
170!
139
        var entiyBaseClass = _options.Data.Entity.BaseClass;
170✔
140

141
        var mappingName = entityClass + "Map";
170✔
142
        mappingName = _namer.UniqueClassName(mappingName);
170✔
143

144
        var mappingNamespace = _options.Data.Mapping.Namespace ?? "Data.Mapping";
170!
145

146
        var contextName = ContextName(entityClass);
170✔
147
        contextName = ToPropertyName(entityContext.ContextClass, contextName);
170✔
148
        contextName = _namer.UniqueContextName(contextName);
170✔
149

150
        entity.EntityClass = entityClass;
170✔
151
        entity.EntityNamespace = entityNamespace;
170✔
152
        entity.EntityBaseClass = entiyBaseClass;
170✔
153

154
        entity.MappingClass = mappingName;
170✔
155
        entity.MappingNamespace = mappingNamespace;
170✔
156

157
        entity.ContextProperty = contextName;
170✔
158

159
        entity.IsView = relationSchema is View;
170✔
160

161
        if (relationSchema is not Table tableSchema)
170✔
162
            return entity;
6✔
163

164
        var isTemporal = tableSchema.Options.IsTemporalTable
164✔
165
            && tableSchema.Options.HistoryTable != null
164✔
166
            && tableSchema.Options.PeriodStartColumnName.HasValue()
164✔
167
            && tableSchema.Options.PeriodEndColumnName.HasValue();
164✔
168

169
        if (isTemporal && _options.Data.Mapping.Temporal)
164!
170
        {
171
            entity.TemporalTableName = tableSchema.Options.HistoryTable?.Name;
1!
172
            entity.TemporalTableSchema = tableSchema.Options.HistoryTable?.Schema;
1!
173

174
            var periodStartColumnName = tableSchema.Options.PeriodStartColumnName;
1✔
175
            var periodEndColumnName = tableSchema.Options.PeriodEndColumnName;
1✔
176

177
            entity.TemporalStartColumn = periodStartColumnName;
1✔
178
            entity.TemporalEndColumn = periodEndColumnName;
1✔
179

180
            var temporalStartProperty = ToPropertyName(entity.EntityClass, periodStartColumnName!);
1✔
181
            temporalStartProperty = _namer.UniqueName(entity.EntityClass, temporalStartProperty);
1✔
182

183
            entity.TemporalStartProperty = temporalStartProperty;
1✔
184

185
            var temporalEndProperty = ToPropertyName(entity.EntityClass, periodEndColumnName!);
1✔
186
            temporalEndProperty = _namer.UniqueName(entity.EntityClass, temporalEndProperty);
1✔
187

188
            entity.TemporalEndProperty = temporalEndProperty;
1✔
189
        }
190

191
        return entity;
164✔
192
    }
193

194

195
    private void CreateProperties(Entity entity, RelationBase relationSchema)
196
    {
197
        var columns = relationSchema.Columns;
170✔
198
        foreach (var column in columns)
2,942✔
199
        {
200
            var parentRelation = column.Parent;
1,301✔
201
            if (parentRelation is null || IsIgnored(column, _options.Database.Exclude.Columns))
1,301!
202
            {
UNCOV
203
                _logger.LogDebug("  Skipping Column : {Schema}.{Table}.{Column}", parentRelation?.Schema, parentRelation?.Name, column.Name);
×
UNCOV
204
                continue;
×
205
            }
206

207
            var property = entity.Properties.ByColumn(column.Name);
1,301✔
208

209
            if (property == null)
1,301✔
210
            {
211
                property = new Property
1,301✔
212
                {
1,301✔
213
                    Entity = entity,
1,301✔
214
                    ColumnName = column.Name
1,301✔
215
                };
1,301✔
216
                entity.Properties.Add(property);
1,301✔
217
            }
218

219
            string name = ToPropertyName(entity.EntityClass, column.Name);
1,301✔
220
            string propertyName = name;
1,301✔
221

222
            foreach (var selection in _options.Data.Entity.Renaming.Properties.Where(p => p.Expression.HasValue()))
2,602!
223
            {
UNCOV
224
                if (selection.Expression.IsNullOrEmpty())
×
225
                    continue;
226

UNCOV
227
                propertyName = Regex.Replace(propertyName, selection.Expression, string.Empty);
×
228
            }
229

230
            // make sure regex doesn't remove everything
231
            if (propertyName.IsNullOrEmpty())
1,301!
UNCOV
232
                propertyName = name;
×
233

234
            propertyName = _namer.UniqueName(entity.EntityClass, propertyName);
1,301✔
235

236
            property.PropertyName = propertyName;
1,301✔
237
            property.NativeType = column.NativeTypeName;
1,301✔
238
            property.DataType = column.DbType;
1,301✔
239
            property.SystemType = column.SystemType;
1,301✔
240
            property.SystemTypeName = GetSystemTypeName(column.NativeTypeName, column.SystemType);
1,301✔
241
            property.Size = column.MaxLength;
1,301✔
242

243
            // overwrite row version type
244
            if (property.IsRowVersion == true
1,301!
245
                && _options.Data.Mapping.RowVersion != RowVersionMapping.ByteArray
1,301✔
246
                && property.SystemType == typeof(byte[]))
1,301✔
247
            {
UNCOV
248
                property.SystemType = _options.Data.Mapping.RowVersion switch
×
UNCOV
249
                {
×
250
                    RowVersionMapping.ByteArray => typeof(byte[]),
×
251
                    RowVersionMapping.Long => typeof(long),
×
252
                    RowVersionMapping.ULong => typeof(ulong),
×
253
                    _ => typeof(byte[])
×
254
                };
×
255
                property.SystemTypeName = property.SystemType.ToType();
×
256
            }
257

258
            //property.DefaultValue = column.DefaultValue;
259
            property.Default = column.DefaultValueSql;
1,301✔
260

261
            property.IsComputed = column.IsComputed;
1,301✔
262
            property.IsIdentity = column.IsIdentity;
1,301✔
263
            property.IsNullable = column.IsNullable;
1,301✔
264
            property.IsRowVersion = column.IsRowVersion;
1,301✔
265
            property.IsConcurrencyToken = column.IsConcurrencyToken;
1,301✔
266

267
            var parentTable = parentRelation as Table;
1,301✔
268
            if (parentTable != null)
1,301✔
269
            {
270
                property.IsPrimaryKey = parentTable.PrimaryKey?.Columns
1,284✔
271
                    .Any(c => c.ColumnName == column.Name) == true;
1,284✔
272

273
                property.IsForeignKey = parentTable.ForeignKeys
1,284✔
274
                    .Any(c => c.ColumnMappings.Any(col => col.DependentColumnName == column.Name));
1,284✔
275

276
                property.IsUnique = parentTable.UniqueConstraints.Any(c => c.Columns.Any(col => col.ColumnName == column.Name))
1,284✔
277
                                    || parentTable.Indexes.Where(i => i.IsUnique).Any(c => c.Columns.Any(col => col.ColumnName == column.Name));
1,284✔
278
            }
279

280
            property.IsProcessed = true;
1,301✔
281
        }
282

283
        entity.Properties.IsProcessed = true;
170✔
284

285
        var table = relationSchema as Table;
170✔
286
        if (table is null)
170✔
287
            return;
6✔
288

289
        bool? isTemporal =   table.Options.IsTemporalTable;
164✔
290
        if (isTemporal != true || _options.Data.Mapping.Temporal)
164!
291
            return;
164✔
292

293
        // add temporal period columns
294
        var temporalStartColumn = table.Options.PeriodStartColumnName;
×
UNCOV
295
        var temporalEndColumn = table.Options.PeriodEndColumnName;
×
296

UNCOV
297
        if (temporalStartColumn.IsNullOrEmpty() || temporalEndColumn.IsNullOrEmpty())
×
298
            return;
×
299

UNCOV
300
        var temporalStart = entity.Properties.ByColumn(temporalStartColumn);
×
301

302
        if (temporalStart == null)
×
303
        {
304
            temporalStart = new Property { Entity = entity, ColumnName = temporalStartColumn };
×
305
            entity.Properties.Add(temporalStart);
×
306
        }
307

308
        temporalStart.PropertyName = ToPropertyName(entity.EntityClass, temporalStartColumn);
×
309
        temporalStart.IsComputed = true;
×
UNCOV
310
        temporalStart.NativeType = "datetime2";
×
UNCOV
311
        temporalStart.DataType = DbType.DateTime2;
×
UNCOV
312
        temporalStart.SystemType = typeof(DateTime);
×
UNCOV
313
        temporalStart.SystemTypeName = typeof(DateTime).ToType();
×
314

UNCOV
315
        temporalStart.IsProcessed = true;
×
316

UNCOV
317
        var temporalEnd = entity.Properties.ByColumn(temporalEndColumn);
×
318

319
        if (temporalEnd == null)
×
320
        {
UNCOV
321
            temporalEnd = new Property { Entity = entity, ColumnName = temporalEndColumn };
×
UNCOV
322
            entity.Properties.Add(temporalEnd);
×
323
        }
324

325
        temporalEnd.PropertyName = ToPropertyName(entity.EntityClass, temporalEndColumn);
×
326
        temporalEnd.IsComputed = true;
×
UNCOV
327
        temporalEnd.NativeType = "datetime2";
×
UNCOV
328
        temporalEnd.DataType = DbType.DateTime2;
×
UNCOV
329
        temporalEnd.SystemType = typeof(DateTime);
×
UNCOV
330
        temporalEnd.SystemTypeName = typeof(DateTime).ToType();
×
331

UNCOV
332
        temporalEnd.IsProcessed = true;
×
UNCOV
333
    }
×
334

335

336
    private void CreateRelationships(EntityContext entityContext, Entity entity, Table tableSchema)
337
    {
338
        foreach (var foreignKey in tableSchema.ForeignKeys.OrderBy(fk => fk.Name))
482✔
339
        {
340
            // skip relationship if principal table is ignored
341
            if (IsIgnored(foreignKey.PrincipalTable, _options.Database.Exclude.Tables))
77!
342
            {
UNCOV
343
                _logger.LogDebug("  Skipping Relationship : {name}", foreignKey.Name);
×
UNCOV
344
                continue;
×
345
            }
346

347
            if (IsIgnored(foreignKey, _options.Database.Exclude.Relationships))
77!
348
            {
UNCOV
349
                _logger.LogDebug("  Skipping Relationship : {name}", foreignKey.Name);
×
350
                continue;
×
351
            }
352

353
            CreateRelationship(entityContext, entity, foreignKey);
77✔
354
        }
355

356
        entity.Relationships.IsProcessed = true;
164✔
357
    }
164✔
358

359
    private void CreateRelationship(EntityContext entityContext, Entity foreignEntity, ForeignKey tableKeySchema)
360
    {
361
        Entity primaryEntity = GetEntity(entityContext, tableKeySchema.PrincipalTable, false, false);
77✔
362

363
        var primaryName = primaryEntity.EntityClass;
77✔
364
        var foreignName = foreignEntity.EntityClass;
77✔
365

366
        var foreignMembers = GetKeyMembers(foreignEntity, tableKeySchema.ColumnMappings.Select(c => c.DependentColumn), tableKeySchema.Name);
77✔
367
        bool foreignMembersRequired = foreignMembers.Any(c => c.IsRequired);
77✔
368

369
        var primaryMembers = GetKeyMembers(primaryEntity, tableKeySchema.ColumnMappings.Select(c => c.PrincipalColumn), tableKeySchema.Name);
77✔
370
        bool primaryMembersRequired = primaryMembers.Any(c => c.IsRequired);
77✔
371

372
        // skip invalid fkeys
373
        if (foreignMembers.Count == 0 || primaryMembers.Count == 0)
77!
UNCOV
374
            return;
×
375

376
        var relationshipName = tableKeySchema.Name;
77✔
377

378
        // ensure relationship name for sync support
379
        if (relationshipName.IsNullOrEmpty())
77!
UNCOV
380
            relationshipName = $"FK_{foreignName}_{primaryName}_{primaryMembers.Select(p => p.PropertyName).ToDelimitedString("_")}";
×
381

382
        relationshipName = _namer.UniqueRelationshipName(relationshipName);
77✔
383

384
        var foreignRelationship = foreignEntity.Relationships
77✔
385
            .FirstOrDefault(r => r.RelationshipName == relationshipName && r.IsForeignKey);
77✔
386

387
        if (foreignRelationship == null)
77!
388
        {
389
            foreignRelationship = new Relationship { RelationshipName = relationshipName };
77✔
390
            foreignEntity.Relationships.Add(foreignRelationship);
77✔
391
        }
392
        foreignRelationship.IsMapped = true;
77✔
393
        foreignRelationship.IsForeignKey = true;
77✔
394
        foreignRelationship.Cardinality = foreignMembersRequired ? Cardinality.One : Cardinality.ZeroOrOne;
77✔
395

396
        foreignRelationship.PrimaryEntity = primaryEntity;
77✔
397
        foreignRelationship.PrimaryProperties = [.. primaryMembers];
77✔
398

399
        foreignRelationship.Entity = foreignEntity;
77✔
400
        foreignRelationship.Properties = [.. foreignMembers];
77✔
401

402
        string prefix = GetMemberPrefix(foreignRelationship, primaryName, foreignName);
77✔
403

404
        string foreignPropertyName = ToPropertyName(foreignEntity.EntityClass, prefix + primaryName);
77✔
405
        foreignPropertyName = _namer.UniqueName(foreignEntity.EntityClass, foreignPropertyName);
77✔
406
        foreignRelationship.PropertyName = foreignPropertyName;
77✔
407

408
        // add reverse
409
        var primaryRelationship = primaryEntity.Relationships
77✔
410
            .FirstOrDefault(r => r.RelationshipName == relationshipName && !r.IsForeignKey);
77✔
411

412
        if (primaryRelationship == null)
77!
413
        {
414
            primaryRelationship = new Relationship { RelationshipName = relationshipName };
77✔
415
            primaryEntity.Relationships.Add(primaryRelationship);
77✔
416
        }
417

418
        primaryRelationship.IsMapped = false;
77✔
419
        primaryRelationship.IsForeignKey = false;
77✔
420

421
        primaryRelationship.PrimaryEntity = foreignEntity;
77✔
422
        primaryRelationship.PrimaryProperties = [.. foreignMembers];
77✔
423

424
        primaryRelationship.Entity = primaryEntity;
77✔
425
        primaryRelationship.Properties = [.. primaryMembers];
77✔
426

427
        bool isOneToOne = IsOneToOne(tableKeySchema, foreignRelationship);
77✔
428
        if (isOneToOne)
77✔
429
            primaryRelationship.Cardinality = primaryMembersRequired ? Cardinality.One : Cardinality.ZeroOrOne;
6!
430
        else
431
            primaryRelationship.Cardinality = Cardinality.Many;
71✔
432

433
        string primaryPropertyName = prefix + foreignName;
77✔
434
        if (!isOneToOne)
77✔
435
            primaryPropertyName = RelationshipName(primaryPropertyName);
71✔
436

437
        primaryPropertyName = ToPropertyName(primaryEntity.EntityClass, primaryPropertyName);
77✔
438
        primaryPropertyName = _namer.UniqueName(primaryEntity.EntityClass, primaryPropertyName);
77✔
439

440
        primaryRelationship.PropertyName = primaryPropertyName;
77✔
441

442
        foreignRelationship.PrimaryPropertyName = primaryRelationship.PropertyName;
77✔
443
        foreignRelationship.PrimaryCardinality = primaryRelationship.Cardinality;
77✔
444

445
        primaryRelationship.PrimaryPropertyName = foreignRelationship.PropertyName;
77✔
446
        primaryRelationship.PrimaryCardinality = foreignRelationship.Cardinality;
77✔
447

448
        foreignRelationship.IsProcessed = true;
77✔
449
        primaryRelationship.IsProcessed = true;
77✔
450
    }
77✔
451

452

453
    private static void CreateMethods(Entity entity, Table tableSchema)
454
    {
455
        if (tableSchema.PrimaryKey != null)
164✔
456
        {
457
            var method = GetMethodFromColumns(entity, tableSchema.PrimaryKey.Columns.Select(c => c.Column));
146✔
458
            if (method != null)
146!
459
            {
460
                method.IsKey = true;
146✔
461
                method.SourceName = tableSchema.PrimaryKey.Name;
146✔
462

463
                if (entity.Methods.All(m => m.NameSuffix != method.NameSuffix))
146!
464
                    entity.Methods.Add(method);
146✔
465
            }
466
        }
467

468
        GetIndexMethods(entity, tableSchema);
164✔
469
        GetForeignKeyMethods(entity, tableSchema);
164✔
470

471
        entity.Methods.IsProcessed = true;
164✔
472
    }
164✔
473

474
    private static void GetForeignKeyMethods(Entity entity, Table table)
475
    {
476
        var columns = new List<Column>();
164✔
477

478
        foreach (var column in table.ForeignKeys.SelectMany(c => c.ColumnMappings.Select(m => m.DependentColumn)))
488✔
479
        {
480
            columns.Add(column);
80✔
481

482
            var method = GetMethodFromColumns(entity, columns);
80✔
483
            if (method != null && entity.Methods.All(m => m.NameSuffix != method.NameSuffix))
80✔
484
                entity.Methods.Add(method);
41✔
485

486
            columns.Clear();
80✔
487
        }
488
    }
164✔
489

490
    private static void GetIndexMethods(Entity entity, Table table)
491
    {
492
        foreach (var index in table.Indexes)
466✔
493
        {
494
            var method = GetMethodFromColumns(entity, index.Columns.Select(c => c.Column));
69✔
495
            if (method == null)
69✔
496
                continue;
497

498
            method.SourceName = index.Name;
67✔
499
            method.IsUnique = index.IsUnique;
67✔
500
            method.IsIndex = true;
67✔
501

502
            if (entity.Methods.All(m => m.NameSuffix != method.NameSuffix))
67✔
503
                entity.Methods.Add(method);
67✔
504
        }
505
    }
164✔
506

507
    private static Method? GetMethodFromColumns(Entity entity, IEnumerable<Column> columns)
508
    {
509
        var method = new Method { Entity = entity };
295✔
510
        var methodName = new StringBuilder();
295✔
511

512
        foreach (var column in columns)
1,238✔
513
        {
514
            var property = entity.Properties.ByColumn(column.Name);
324✔
515
            if (property == null)
324✔
516
                continue;
517

518
            method.Properties.Add(property);
324✔
519
            methodName.Append(property.PropertyName);
324✔
520
        }
521

522
        if (method.Properties.Count == 0)
295✔
523
            return null;
2✔
524

525
        method.NameSuffix = methodName.ToString();
293✔
526
        return method;
293✔
527
    }
528

529

530
    private void GetModels(Entity? entity)
531
    {
532
        if (entity == null || entity.Models.IsProcessed)
170!
UNCOV
533
            return;
×
534

535
        _options.Variables.Set(entity);
170✔
536

537
        if (_options.Model.Read.Generate)
170✔
538
            CreateModel(entity, _options.Model.Read, ModelType.Read);
141✔
539
        if (!entity.IsView && _options.Model.Create.Generate)
170✔
540
            CreateModel(entity, _options.Model.Create, ModelType.Create);
134✔
541
        if (!entity.IsView && _options.Model.Update.Generate)
170✔
542
            CreateModel(entity, _options.Model.Update, ModelType.Update);
134✔
543

544
        if (entity.Models.Count > 0)
170✔
545
        {
546
            var mapperNamespace = _options.Model.Mapper.Namespace ?? "Data.Mapper";
141!
547

548
            var mapperClass = ToLegalName(_options.Model.Mapper.Name);
141✔
549
            mapperClass = _namer.UniqueModelName(mapperNamespace, mapperClass);
141✔
550

551
            entity.MapperClass = mapperClass;
141✔
552
            entity.MapperNamespace = mapperNamespace;
141✔
553
            entity.MapperBaseClass = _options.Model.Mapper.BaseClass;
141✔
554
        }
555

556
        _options.Variables.Remove(entity);
170✔
557

558
        entity.Models.IsProcessed = true;
170✔
559
    }
170✔
560

561
    private void CreateModel<TOption>(Entity entity, TOption options, ModelType modelType)
562
        where TOption : ModelOptionsBase
563
    {
564
        if (IsIgnored(entity, options, _options.Model.Shared))
409✔
565
            return;
8✔
566

567
        var modelNamespace = options.Namespace.HasValue()
401!
568
            ? options.Namespace
401✔
569
            : _options.Model.Shared.Namespace;
401✔
570

571
        var modelHeader = options.Header.HasValue()
401!
572
            ? options.Header
401✔
573
            : _options.Model.Shared.Header;
401✔
574

575
        modelNamespace ??= "Data.Models";
401!
576

577
        var modelClass = ToLegalName(options.Name);
401✔
578
        modelClass = _namer.UniqueModelName(modelNamespace, modelClass);
401✔
579

580
        var model = new Model
401✔
581
        {
401✔
582
            Entity = entity,
401✔
583
            ModelType = modelType,
401✔
584
            ModelBaseClass = options.BaseClass,
401✔
585
            ModelNamespace = modelNamespace,
401✔
586
            ModelClass = modelClass,
401✔
587
            ModelAttributes = options.Attributes,
401✔
588
            ModelHeader = modelHeader
401✔
589
        };
401✔
590

591
        foreach (var property in entity.Properties)
6,918✔
592
        {
593
            if (IsIgnored(property, options, _options.Model.Shared))
3,058✔
594
                continue;
595

596
            model.Properties.Add(property);
3,022✔
597
        }
598

599
        _options.Variables.Set(model);
401✔
600

601
        var validatorNamespace = _options.Model.Validator.Namespace ?? "Data.Validation";
401!
602
        var validatorClass = ToLegalName(_options.Model.Validator.Name);
401✔
603
        validatorClass = _namer.UniqueModelName(validatorNamespace, validatorClass);
401✔
604

605
        model.ValidatorBaseClass = _options.Model.Validator.BaseClass;
401✔
606
        model.ValidatorClass = validatorClass;
401✔
607
        model.ValidatorNamespace = validatorNamespace;
401✔
608

609
        entity.Models.Add(model);
401✔
610

611
        _options.Variables.Remove(model);
401✔
612
    }
401✔
613

614

615
    private List<Property> GetKeyMembers(Entity entity, IEnumerable<Column> members, string? relationshipName)
616
    {
617
        var keyMembers = new List<Property>();
154✔
618

619
        foreach (var member in members)
628✔
620
        {
621
            var property = entity.Properties.ByColumn(member.Name);
160✔
622

623
            if (property == null)
160!
UNCOV
624
                _logger.LogWarning("Could not find column {columnName} for relationship {relationshipName}.", member.Name, relationshipName);
×
625
            else
626
                keyMembers.Add(property);
160✔
627
        }
628

629
        return keyMembers;
154✔
630
    }
631

632
    private string GetSystemTypeName(string? nativeType, Type systemType)
633
    {
634
        var mapping = _options.Data.Entity.TypeMapping
1,301✔
635
            .FirstOrDefault(m => string.Equals(m.NativeType, nativeType, StringComparison.OrdinalIgnoreCase));
1,301✔
636

637
        if (mapping?.SystemType.HasValue() == true)
1,301✔
638
            return mapping.SystemType;
11✔
639

640
        return systemType.ToType();
1,290✔
641
    }
642

643
    private static string GetMemberPrefix(Relationship relationship, string primaryClass, string foreignClass)
644
    {
645
        string thisKey = relationship.Properties
77!
646
            .Select(p => p.PropertyName)
77✔
647
            .FirstOrDefault() ?? string.Empty;
77✔
648

649
        string otherKey = relationship.PrimaryProperties
77!
650
            .Select(p => p.PropertyName)
77✔
651
            .FirstOrDefault() ?? string.Empty;
77✔
652

653
        bool isSameName = thisKey.Equals(otherKey, StringComparison.OrdinalIgnoreCase);
77✔
654
        isSameName = (isSameName || thisKey.Equals(primaryClass + otherKey, StringComparison.OrdinalIgnoreCase));
77✔
655

656
        string prefix = string.Empty;
77✔
657
        if (isSameName)
77✔
658
            return prefix;
44✔
659

660
        prefix = thisKey.Replace(otherKey, "");
33✔
661
        prefix = prefix.Replace(primaryClass, "");
33✔
662
        prefix = prefix.Replace(foreignClass, "");
33✔
663
        prefix = IdSuffixRegex().Replace(prefix, "");
33✔
664
        prefix = DigitPrefixRegex().Replace(prefix, "");
33✔
665

666
        return prefix;
33✔
667
    }
668

669
    private static bool IsOneToOne(ForeignKey tableKeySchema, Relationship foreignRelationship)
670
    {
671
        var foreignColumn = foreignRelationship.Properties
77✔
672
            .Select(p => p.ColumnName)
77✔
673
            .FirstOrDefault();
77✔
674

675
        return tableKeySchema.PrincipalTable.PrimaryKey != null
77✔
676
            && tableKeySchema.DependentTable.PrimaryKey != null
77✔
677
            && tableKeySchema.DependentTable.PrimaryKey.Columns.Count == 1
77✔
678
            && tableKeySchema.DependentTable.PrimaryKey.Columns.Any(c => c.ColumnName == foreignColumn);
77✔
679

680
        // if f.key is unique
681
        //return tableKeySchema.ForeignKeyMemberColumns.All(column => column.IsUnique);
682
    }
683

684

685
    private string RelationshipName(string name)
686
    {
687
        var naming = _options.Data.Entity.RelationshipNaming;
71✔
688
        if (naming == RelationshipNaming.Preserve)
71!
UNCOV
689
            return name;
×
690

691
        if (naming == RelationshipNaming.Suffix)
71!
UNCOV
692
            return name + "List";
×
693

694
        return name.Pluralize(false);
71✔
695
    }
696

697
    private string ContextName(string name)
698
    {
699
        var naming = _options.Data.Context.PropertyNaming;
170✔
700
        if (naming == ContextNaming.Preserve)
170!
UNCOV
701
            return name;
×
702

703
        if (naming == ContextNaming.Suffix)
170!
UNCOV
704
            return name + "DataSet";
×
705

706
        return name.Pluralize(false);
170✔
707
    }
708

709
    private string EntityName(string name)
710
    {
711
        var tableNaming = _options.Database.TableNaming;
170✔
712
        var entityNaming = _options.Data.Entity.EntityNaming;
170✔
713

714
        if (tableNaming != TableNaming.Plural && entityNaming == EntityNaming.Plural)
170!
UNCOV
715
            name = name.Pluralize(false);
×
716
        else if (tableNaming != TableNaming.Singular && entityNaming == EntityNaming.Singular)
170!
UNCOV
717
            name = name.Singularize(false);
×
718

719
        var rename = name;
170✔
720
        foreach (var selection in _options.Data.Entity.Renaming.Entities.Where(p => p.Expression.HasValue()))
340!
721
        {
UNCOV
722
            if (selection.Expression.IsNullOrEmpty())
×
723
                continue;
724

UNCOV
725
            rename = Regex.Replace(rename, selection.Expression, string.Empty);
×
726
        }
727

728
        // make sure regex doesn't remove everything
729
        return rename.HasValue() ? rename : name;
170!
730
    }
731

732
    private string ToClassName(RelationBase tableSchema)
733
    {
734
        return ToClassName(
170✔
735
            tableSchema.QualifiedName.Name,
170✔
736
            tableSchema.QualifiedName.Schema
170✔
737
        );
170✔
738
    }
739

740
    private string ToClassName(string tableName, string? tableSchema)
741
    {
742
        tableName = EntityName(tableName);
170✔
743
        var className = tableName;
170✔
744

745
        if (_options.Data.Entity.PrefixWithSchemaName && tableSchema != null)
170!
746
            className = $"{tableSchema}{tableName}";
2✔
747

748
        return ToLegalName(className);
170✔
749
    }
750

751
    private string ToPropertyName(string className, string name)
752
    {
753
        string propertyName = ToLegalName(name);
1,627✔
754
        if (className.Equals(propertyName, StringComparison.OrdinalIgnoreCase))
1,627✔
755
            propertyName += "Member";
11✔
756

757
        return propertyName;
1,627✔
758
    }
759

760
    private static string ToLegalName(string? name)
761
    {
762
        if (string.IsNullOrWhiteSpace(name))
3,096✔
763
            return string.Empty;
34✔
764

765
        string legalName = name;
3,062✔
766

767
        // remove invalid leading
768
        var expression = LeadingNonAlphaRegex();
3,062✔
769
        if (expression.IsMatch(name))
3,062✔
770
            legalName = expression.Replace(legalName, string.Empty);
13✔
771

772
        // prefix with column when all characters removed
773
        if (legalName.IsNullOrWhiteSpace())
3,062✔
774
            legalName = "Number" + name;
1✔
775

776
        return legalName.ToPascalCase();
3,062✔
777
    }
778

779

780
    private static bool IsIgnored(RelationBase? relation, IEnumerable<MatchOptions> exclude)
781
    {
782
        if (relation is null)
249!
UNCOV
783
            return true;
×
784

785
        var name = relation.QualifiedName;
249✔
786
        var includeExpressions = Enumerable.Empty<MatchOptions>();
249✔
787
        var excludeExpressions = exclude ?? [];
249!
788

789
        return IsIgnored(name, excludeExpressions, includeExpressions);
249✔
790
    }
791

792
    private static bool IsIgnored(Column column, IEnumerable<MatchOptions> exclude)
793
    {
794
        var table = column.Parent;
1,301✔
795
        if (table == null)
1,301!
796
            return true;
×
797

798
        var name = $"{table.Schema}.{table.Name}.{column.Name}";
1,301✔
799
        var includeExpressions = Enumerable.Empty<MatchOptions>();
1,301✔
800
        var excludeExpressions = exclude ?? [];
1,301!
801

802
        return IsIgnored(name, excludeExpressions, includeExpressions);
1,301✔
803
    }
804

805
    private static bool IsIgnored(ForeignKey relationship, IEnumerable<MatchOptions> exclude)
806
    {
807
        var table = relationship.PrincipalTable;
77✔
808
        if (table == null)
77!
809
            return true;
×
810

811
        var name = $"{table.Schema}.{table.Name}.{relationship.Name}";
77✔
812
        var includeExpressions = Enumerable.Empty<MatchOptions>();
77✔
813
        var excludeExpressions = exclude ?? [];
77!
814

815
        return IsIgnored(name, excludeExpressions, includeExpressions);
77✔
816
    }
817

818
    private static bool IsIgnored<TOption>(Property property, TOption options, SharedModelOptions sharedOptions)
819
        where TOption : ModelOptionsBase
820
    {
821
        var name = $"{property.Entity.EntityClass}.{property.PropertyName}";
3,058✔
822

823
        var includeExpressions = new HashSet<MatchOptions>(sharedOptions?.Include?.Properties ?? []);
3,058!
824
        var excludeExpressions = new HashSet<MatchOptions>(sharedOptions?.Exclude?.Properties ?? []);
3,058!
825

826
        var includeProperties = options?.Include?.Properties ?? [];
3,058!
827
        foreach (var expression in includeProperties)
6,116!
UNCOV
828
            includeExpressions.Add(expression);
×
829

830
        var excludeProperties = options?.Exclude?.Properties ?? [];
3,058!
831
        foreach (var expression in excludeProperties)
6,116!
UNCOV
832
            excludeExpressions.Add(expression);
×
833

834
        return IsIgnored(name, excludeExpressions, includeExpressions);
3,058✔
835
    }
836

837
    private static bool IsIgnored<TOption>(Entity entity, TOption options, SharedModelOptions sharedOptions)
838
        where TOption : ModelOptionsBase
839
    {
840
        var name = entity.EntityClass;
409✔
841

842
        var includeExpressions = new HashSet<MatchOptions>(sharedOptions?.Include?.Entities ?? []);
409!
843
        var excludeExpressions = new HashSet<MatchOptions>(sharedOptions?.Exclude?.Entities ?? []);
409!
844

845
        var includeEntities = options?.Include?.Entities ?? [];
409!
846
        foreach (var expression in includeEntities)
818!
UNCOV
847
            includeExpressions.Add(expression);
×
848

849
        var excludeEntities = options?.Exclude?.Entities ?? [];
409!
850
        foreach (var expression in excludeEntities)
1,346✔
851
            excludeExpressions.Add(expression);
264✔
852

853
        return IsIgnored(name, excludeExpressions, includeExpressions);
409✔
854
    }
855

856
    private static bool IsIgnored(string name, IEnumerable<MatchOptions> excludeExpressions, IEnumerable<MatchOptions> includeExpressions)
857
    {
858
        foreach (var expression in includeExpressions)
10,188!
859
        {
UNCOV
860
            if (expression.IsMatch(name))
×
UNCOV
861
                return false;
×
862
        }
863

864
        foreach (var expression in excludeExpressions)
28,966✔
865
        {
866
            if (expression.IsMatch(name))
9,412✔
867
                return true;
46✔
868
        }
869

870
        return false;
5,048✔
871
    }
46✔
872

873

874
    [GeneratedRegex(@"^[^a-zA-Z_]+")]
875
    private static partial Regex LeadingNonAlphaRegex();
876

877
    [GeneratedRegex(@"(_ID|_id|_Id|\.ID|\.id|\.Id|ID|Id)$")]
878
    private static partial Regex IdSuffixRegex();
879

880
    [GeneratedRegex(@"^\d")]
881
    private static partial Regex DigitPrefixRegex();
882
}
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