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

loresoft / EntityFrameworkCore.Generator / 27767067052

18 Jun 2026 02:35PM UTC coverage: 70.481% (-4.9%) from 75.418%
27767067052

push

github

pwelter34
Update SourceSynchronizer.cs

913 of 1785 branches covered (51.15%)

Branch coverage included in aggregate %.

5 of 6 new or added lines in 1 file covered. (83.33%)

297 existing lines in 10 files now uncovered.

4094 of 5319 relevant lines covered (76.97%)

1291.15 hits per line

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

78.56
/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
            {
79
                _logger.LogDebug("  Skipping View : {schema}.{name}", view.Schema, view.Name);
×
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
        var pluralName = entityClass.Pluralize(false);
170✔
138

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

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

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

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

151
        entity.EntityClass = entityClass;
170✔
152
        entity.EntityPlural = pluralName;
170✔
153
        entity.EntityNamespace = entityNamespace;
170✔
154
        entity.EntityBaseClass = entiyBaseClass;
170✔
155

156
        entity.MappingClass = mappingName;
170✔
157
        entity.MappingNamespace = mappingNamespace;
170✔
158

159
        entity.ContextProperty = contextName;
170✔
160

161
        entity.IsView = relationSchema is View;
170✔
162

163
        if (relationSchema is not Table tableSchema)
170✔
164
            return entity;
6✔
165

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

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

176
            var periodStartColumnName = tableSchema.Options.PeriodStartColumnName;
1✔
177
            var periodEndColumnName = tableSchema.Options.PeriodEndColumnName;
1✔
178

179
            entity.TemporalStartColumn = periodStartColumnName;
1✔
180
            entity.TemporalEndColumn = periodEndColumnName;
1✔
181

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

185
            entity.TemporalStartProperty = temporalStartProperty;
1✔
186

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

190
            entity.TemporalEndProperty = temporalEndProperty;
1✔
191
        }
192

193
        return entity;
164✔
194
    }
195

196

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

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

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

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

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

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

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

236
            propertyName = _namer.UniqueName(entity.EntityClass, propertyName);
1,301✔
237

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

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

260
            //property.DefaultValue = column.DefaultValue;
261
            property.Default = column.DefaultValueSql;
1,301✔
262

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

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

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

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

282
            property.IsProcessed = true;
1,301✔
283
        }
284

285
        entity.Properties.IsProcessed = true;
170✔
286

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

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

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

UNCOV
299
        if (temporalStartColumn.IsNullOrEmpty() || temporalEndColumn.IsNullOrEmpty())
×
300
            return;
×
301

302
        var temporalStart = entity.Properties.ByColumn(temporalStartColumn);
×
303

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

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

317
        temporalStart.IsProcessed = true;
×
318

319
        var temporalEnd = entity.Properties.ByColumn(temporalEndColumn);
×
320

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

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

UNCOV
334
        temporalEnd.IsProcessed = true;
×
UNCOV
335
    }
×
336

337

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

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

355
            CreateRelationship(entityContext, entity, foreignKey);
77✔
356
        }
357

358
        entity.Relationships.IsProcessed = true;
164✔
359
    }
164✔
360

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

365
        var primaryName = primaryEntity.EntityClass;
77✔
366
        var foreignName = foreignEntity.EntityClass;
77✔
367

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

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

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

378
        var relationshipName = tableKeySchema.Name;
77✔
379

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

384
        relationshipName = _namer.UniqueRelationshipName(relationshipName);
77✔
385

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

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

398
        foreignRelationship.PrimaryEntity = primaryEntity;
77✔
399
        foreignRelationship.PrimaryProperties = [.. primaryMembers];
77✔
400

401
        foreignRelationship.Entity = foreignEntity;
77✔
402
        foreignRelationship.Properties = [.. foreignMembers];
77✔
403

404
        string prefix = GetMemberPrefix(foreignRelationship, primaryName, foreignName);
77✔
405

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

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

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

420
        primaryRelationship.IsMapped = false;
77✔
421
        primaryRelationship.IsForeignKey = false;
77✔
422

423
        primaryRelationship.PrimaryEntity = foreignEntity;
77✔
424
        primaryRelationship.PrimaryProperties = [.. foreignMembers];
77✔
425

426
        primaryRelationship.Entity = primaryEntity;
77✔
427
        primaryRelationship.Properties = [.. primaryMembers];
77✔
428

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

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

439
        primaryPropertyName = ToPropertyName(primaryEntity.EntityClass, primaryPropertyName);
77✔
440
        primaryPropertyName = _namer.UniqueName(primaryEntity.EntityClass, primaryPropertyName);
77✔
441

442
        primaryRelationship.PropertyName = primaryPropertyName;
77✔
443

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

447
        primaryRelationship.PrimaryPropertyName = foreignRelationship.PropertyName;
77✔
448
        primaryRelationship.PrimaryCardinality = foreignRelationship.Cardinality;
77✔
449

450
        foreignRelationship.IsProcessed = true;
77✔
451
        primaryRelationship.IsProcessed = true;
77✔
452
    }
77✔
453

454

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

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

470
        GetIndexMethods(entity, tableSchema);
164✔
471
        GetForeignKeyMethods(entity, tableSchema);
164✔
472

473
        entity.Methods.IsProcessed = true;
164✔
474
    }
164✔
475

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

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

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

488
            columns.Clear();
80✔
489
        }
490
    }
164✔
491

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

500
            method.SourceName = index.Name;
67✔
501
            method.IsUnique = index.IsUnique;
67✔
502
            method.IsIndex = true;
67✔
503

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

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

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

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

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

527
        method.NameSuffix = methodName.ToString();
293✔
528
        return method;
293✔
529
    }
530

531

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

537
        _options.Variables.Set(entity);
170✔
538

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

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

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

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

558
        _options.Variables.Remove(entity);
170✔
559

560
        entity.Models.IsProcessed = true;
170✔
561
    }
170✔
562

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

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

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

577
        modelNamespace ??= "Data.Models";
401!
578

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

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

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

598
            model.Properties.Add(property);
3,022✔
599
        }
600

601
        _options.Variables.Set(model);
401✔
602

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

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

611
        entity.Models.Add(model);
401✔
612

613
        _options.Variables.Remove(model);
401✔
614
    }
401✔
615

616

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

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

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

631
        return keyMembers;
154✔
632
    }
633

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

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

642
        return systemType.ToType();
1,290✔
643
    }
644

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

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

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

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

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

668
        return prefix;
33✔
669
    }
670

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

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

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

686

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

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

696
        return name.Pluralize(false);
71✔
697
    }
698

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

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

708
        return name.Pluralize(false);
170✔
709
    }
710

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

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

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

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

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

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

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

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

750
        return ToLegalName(className);
170✔
751
    }
752

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

759
        return propertyName;
1,627✔
760
    }
761

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

767
        string legalName = name;
3,062✔
768

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

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

778
        return legalName.ToPascalCase();
3,062✔
779
    }
780

781

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

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

791
        return IsIgnored(name, excludeExpressions, includeExpressions);
249✔
792
    }
793

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

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

804
        return IsIgnored(name, excludeExpressions, includeExpressions);
1,301✔
805
    }
806

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

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

817
        return IsIgnored(name, excludeExpressions, includeExpressions);
77✔
818
    }
819

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

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

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

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

836
        return IsIgnored(name, excludeExpressions, includeExpressions);
3,058✔
837
    }
838

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

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

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

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

855
        return IsIgnored(name, excludeExpressions, includeExpressions);
409✔
856
    }
857

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

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

872
        return false;
5,048✔
873
    }
46✔
874

875

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

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

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