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

loresoft / EntityFrameworkCore.Generator / 27760980405

18 Jun 2026 12:56PM UTC coverage: 75.418% (+0.7%) from 74.693%
27760980405

push

github

pwelter34
fix build

1007 of 1723 branches covered (58.44%)

Branch coverage included in aggregate %.

4221 of 5209 relevant lines covered (81.03%)

1242.99 hits per line

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

90.17
/src/EntityFrameworkCore.Generator.Core/Templates/MappingClassTemplate.cs
1
using System.Data;
2
using System.Globalization;
3

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

8
namespace EntityFrameworkCore.Generator.Templates;
9

10
public class MappingClassTemplate : CodeTemplateBase
11
{
12
    private readonly Entity _entity;
13

14
    public MappingClassTemplate(Entity entity, GeneratorOptions options) : base(options)
160✔
15
    {
16
        _entity = entity;
160✔
17
    }
160✔
18

19

20
    public override string WriteCode()
21
    {
22
        CodeBuilder.Clear();
160✔
23

24
        if (Options.Data.Mapping.Header.HasValue())
160!
25
            CodeBuilder.AppendLine(Options.Data.Mapping.Header).AppendLine();
×
26

27
        CodeBuilder.AppendLine("using System;");
160✔
28
        CodeBuilder.AppendLine("using System.Collections.Generic;");
160✔
29
        CodeBuilder.AppendLine();
160✔
30
        CodeBuilder.AppendLine("using Microsoft.EntityFrameworkCore;");
160✔
31
        CodeBuilder.AppendLine("using Microsoft.EntityFrameworkCore.Metadata.Builders;");
160✔
32
        CodeBuilder.AppendLine();
160✔
33

34
        CodeBuilder.Append($"namespace {_entity.MappingNamespace}");
160✔
35

36
        if (Options.Project.FileScopedNamespace)
160✔
37
        {
38
            CodeBuilder.AppendLine(";");
137✔
39
            CodeBuilder.AppendLine();
137✔
40
            GenerateClass();
137✔
41
        }
42
        else
43
        {
44
            CodeBuilder.AppendLine();
23✔
45
            CodeBuilder.AppendLine("{");
23✔
46

47
            using (CodeBuilder.Indent())
23✔
48
            {
49
                GenerateClass();
23✔
50
            }
23✔
51

52
            CodeBuilder.AppendLine("}");
23✔
53
        }
54

55
        return CodeBuilder.ToString();
160✔
56
    }
57

58

59
    private void GenerateClass()
60
    {
61
        var mappingClass = _entity.MappingClass.ToSafeName();
160✔
62
        var entityClass = _entity.EntityClass.ToSafeName();
160✔
63
        var safeName = $"{_entity.EntityNamespace}.{entityClass}";
160✔
64

65
        if (Options.Data.Mapping.Document)
160✔
66
        {
67
            GenerateClassDocumentation(safeName);
138✔
68
        }
69

70
        CodeBuilder.AppendLine($"public partial class {mappingClass}");
160✔
71

72
        using (CodeBuilder.Indent())
160✔
73
            CodeBuilder.AppendLine($": IEntityTypeConfiguration<{safeName}>");
160✔
74

75
        CodeBuilder.AppendLine("{");
160✔
76

77
        using (CodeBuilder.Indent())
160✔
78
        {
79
            GenerateConfigure();
160✔
80
            GenerateConstants();
160✔
81
        }
160✔
82

83
        CodeBuilder.AppendLine("}");
160✔
84

85
    }
160✔
86

87
    private void GenerateConstants()
88
    {
89
        var entityClass = _entity.EntityClass.ToSafeName();
160✔
90
        var safeName = $"{_entity.EntityNamespace}.{entityClass}";
160✔
91

92
        CodeBuilder.AppendLine("#region Generated Constants");
160✔
93

94
        if (Options.Data.Mapping.Document)
160✔
95
            GenerateTableConstantsDocumentation(safeName);
138✔
96

97
        CodeBuilder.AppendLine("public readonly struct Table");
160✔
98
        CodeBuilder.AppendLine("{");
160✔
99

100
        using (CodeBuilder.Indent())
160✔
101
        {
102

103
            if (_entity.TableSchema.HasValue())
160✔
104
            {
105
                if (Options.Data.Mapping.Document)
126✔
106
                    GenerateTableSchemaDocumentation(safeName);
104✔
107

108
                CodeBuilder.AppendLine($"public const string Schema = \"{_entity.TableSchema}\";");
126✔
109
            }
110

111
            if (Options.Data.Mapping.Document)
160✔
112
                GenerateTableNameDocumentation(safeName);
138✔
113

114
            CodeBuilder.AppendLine($"public const string Name = \"{_entity.TableName}\";");
160✔
115
        }
160✔
116

117
        CodeBuilder.AppendLine("}");
160✔
118

119
        CodeBuilder.AppendLine();
160✔
120

121
        if (Options.Data.Mapping.Document)
160✔
122
            GenerateColumnConstantsDocumentation(safeName);
138✔
123

124
        CodeBuilder.AppendLine("public readonly struct Columns");
160✔
125
        CodeBuilder.AppendLine("{");
160✔
126

127
        using (CodeBuilder.Indent())
160✔
128
        {
129
            foreach (var property in _entity.Properties)
2,880✔
130
            {
131
                if (Options.Data.Mapping.Document)
1,280✔
132
                    GenerateColumnNameDocumentation(safeName, property);
1,070✔
133

134
                CodeBuilder.AppendLine($"public const string {property.PropertyName.ToSafeName()} = {property.ColumnName.ToLiteral()};");
1,280✔
135
            }
136
        }
137

138
        CodeBuilder.AppendLine("}");
160✔
139
        CodeBuilder.AppendLine("#endregion");
160✔
140
    }
160✔
141

142
    private void GenerateConfigure()
143
    {
144
        var entityClass = _entity.EntityClass.ToSafeName();
160✔
145
        var entityFullName = $"{_entity.EntityNamespace}.{entityClass}";
160✔
146

147
        if (Options.Data.Mapping.Document)
160✔
148
        {
149
            GenerateConfigureDocumentation(entityFullName);
138✔
150
        }
151

152
        CodeBuilder.AppendLine($"public void Configure(EntityTypeBuilder<{entityFullName}> builder)");
160✔
153
        CodeBuilder.AppendLine("{");
160✔
154

155
        using (CodeBuilder.Indent())
160✔
156
        {
157
            CodeBuilder.AppendLine("#region Generated Configure");
160✔
158

159
            GenerateTableMapping();
160✔
160
            GenerateKeyMapping();
160✔
161
            GeneratePropertyMapping();
160✔
162
            GenerateRelationshipMapping();
160✔
163

164
            CodeBuilder.AppendLine("#endregion");
160✔
165
        }
160✔
166

167
        CodeBuilder.AppendLine("}");
160✔
168
        CodeBuilder.AppendLine();
160✔
169
    }
160✔
170

171
    private void GenerateClassDocumentation(string entityFullName)
172
    {
173
        var sourceName = ToXmlText(GetQualifiedTableName());
138✔
174
        var sourceType = _entity.IsView ? "view" : "table";
138✔
175

176
        CodeBuilder.AppendLine("/// <summary>");
138✔
177

178
        if (sourceName.HasValue())
138!
179
            CodeBuilder.AppendLine($"/// Configures Entity Framework Core mapping for the <see cref=\"{entityFullName}\" /> entity mapped to the <c>{sourceName}</c> {sourceType}.");
138✔
180
        else
181
            CodeBuilder.AppendLine($"/// Configures Entity Framework Core mapping for the <see cref=\"{entityFullName}\" /> entity.");
×
182

183
        CodeBuilder.AppendLine("/// </summary>");
138✔
184
    }
138✔
185

186
    private void GenerateConfigureDocumentation(string entityFullName)
187
    {
188
        CodeBuilder.AppendLine("/// <summary>");
138✔
189
        CodeBuilder.AppendLine($"/// Configures the table, key, property, and relationship mappings for <see cref=\"{entityFullName}\" />.");
138✔
190
        CodeBuilder.AppendLine("/// </summary>");
138✔
191
        CodeBuilder.AppendLine($"/// <param name=\"builder\">The builder used to configure <see cref=\"{entityFullName}\" />.</param>");
138✔
192
    }
138✔
193

194
    private void GenerateTableConstantsDocumentation(string entityFullName)
195
    {
196
        CodeBuilder.AppendLine("/// <summary>");
138✔
197
        CodeBuilder.AppendLine($"/// Contains table mapping constants for <see cref=\"{entityFullName}\" />.");
138✔
198
        CodeBuilder.AppendLine("/// </summary>");
138✔
199
    }
138✔
200

201
    private void GenerateTableSchemaDocumentation(string entityFullName)
202
    {
203
        CodeBuilder.AppendLine("/// <summary>");
104✔
204
        CodeBuilder.AppendLine($"/// The database schema name for <see cref=\"{entityFullName}\" />.");
104✔
205
        CodeBuilder.AppendLine("/// </summary>");
104✔
206
    }
104✔
207

208
    private void GenerateTableNameDocumentation(string entityFullName)
209
    {
210
        var sourceType = _entity.IsView ? "view" : "table";
138✔
211

212
        CodeBuilder.AppendLine("/// <summary>");
138✔
213
        CodeBuilder.AppendLine($"/// The database {sourceType} name for <see cref=\"{entityFullName}\" />.");
138✔
214
        CodeBuilder.AppendLine("/// </summary>");
138✔
215
    }
138✔
216

217
    private void GenerateColumnConstantsDocumentation(string entityFullName)
218
    {
219
        CodeBuilder.AppendLine("/// <summary>");
138✔
220
        CodeBuilder.AppendLine($"/// Contains column name constants for <see cref=\"{entityFullName}\" /> properties.");
138✔
221
        CodeBuilder.AppendLine("/// </summary>");
138✔
222
    }
138✔
223

224
    private void GenerateColumnNameDocumentation(string entityFullName, Property property)
225
    {
226
        var propertyName = property.PropertyName.ToSafeName();
1,070✔
227
        var columnName = ToXmlText(property.ColumnName);
1,070✔
228

229
        CodeBuilder.AppendLine("/// <summary>");
1,070✔
230

231
        if (columnName.HasValue())
1,070!
232
            CodeBuilder.AppendLine($"/// The <c>{columnName}</c> column name for <see cref=\"{entityFullName}.{propertyName}\" />.");
1,070✔
233
        else
234
            CodeBuilder.AppendLine($"/// The column name for <see cref=\"{entityFullName}.{propertyName}\" />.");
×
235

236
        CodeBuilder.AppendLine("/// </summary>");
1,070✔
237
    }
1,070✔
238

239
    private string? GetQualifiedTableName()
240
    {
241
        if (_entity.TableName.IsNullOrEmpty())
138!
242
            return _entity.TableName;
×
243

244
        return _entity.TableSchema.HasValue()
138✔
245
            ? $"{_entity.TableSchema}.{_entity.TableName}"
138✔
246
            : _entity.TableName;
138✔
247
    }
248

249

250
    private void GenerateRelationshipMapping()
251
    {
252
        CodeBuilder.AppendLine("// relationships");
160✔
253
        foreach (var relationship in _entity.Relationships.Where(e => e.IsMapped))
474✔
254
        {
255
            GenerateRelationshipMapping(relationship);
77✔
256
            CodeBuilder.AppendLine();
77✔
257
        }
258

259
    }
160✔
260

261
    private void GenerateRelationshipMapping(Relationship relationship)
262
    {
263
        CodeBuilder.Append("builder.HasOne(t => t.");
77✔
264
        CodeBuilder.Append(relationship.PropertyName);
77✔
265
        CodeBuilder.Append(")");
77✔
266
        CodeBuilder.AppendLine();
77✔
267

268
        CodeBuilder.IncrementIndent();
77✔
269

270
        CodeBuilder.Append(relationship.PrimaryCardinality == Cardinality.Many
77✔
271
            ? ".WithMany(t => t."
77✔
272
            : ".WithOne(t => t.");
77✔
273

274
        CodeBuilder.Append(relationship.PrimaryPropertyName);
77✔
275
        CodeBuilder.Append(")");
77✔
276

277
        CodeBuilder.AppendLine();
77✔
278
        CodeBuilder.Append(".HasForeignKey");
77✔
279
        if (relationship.IsOneToOne)
77✔
280
        {
281
            CodeBuilder.Append("<");
6✔
282
            CodeBuilder.Append(_entity.EntityNamespace);
6✔
283
            CodeBuilder.Append(".");
6✔
284
            CodeBuilder.Append(_entity.EntityClass.ToSafeName());
6✔
285
            CodeBuilder.Append(">");
6✔
286
        }
287
        CodeBuilder.Append("(d => ");
77✔
288

289
        var keys = relationship.Properties;
77✔
290
        bool wroteLine = false;
77✔
291

292
        if (keys.Count == 1)
77✔
293
        {
294
            var propertyName = keys[0].PropertyName.ToSafeName();
74✔
295
            CodeBuilder.Append($"d.{propertyName}");
74✔
296
        }
297
        else
298
        {
299
            CodeBuilder.Append("new { ");
3✔
300
            foreach (var p in keys)
18✔
301
            {
302
                if (wroteLine)
6✔
303
                    CodeBuilder.Append(", ");
3✔
304

305
                CodeBuilder.Append($"d.{p.PropertyName}");
6✔
306
                wroteLine = true;
6✔
307
            }
308
            CodeBuilder.Append("}");
3✔
309
        }
310
        CodeBuilder.Append(")");
77✔
311

312
        var primaryKeys = relationship.PrimaryProperties;
77✔
313
        var nonPrimaryPrincipalKey = !primaryKeys
77✔
314
            .Select(pp => relationship.PrimaryEntity.Properties.ByProperty(pp.PropertyName))
77✔
315
            .All(p => p?.IsPrimaryKey ?? true);
77✔
316

317
        if (nonPrimaryPrincipalKey)
77!
318
        {
319
            CodeBuilder.AppendLine();
×
320

321
            CodeBuilder.Append(".HasPrincipalKey(t => ");
×
322
            if (primaryKeys.Count > 1)
×
323
            {
324
                CodeBuilder.Append("new { ");
×
325
                CodeBuilder.Append(string.Join(", ", primaryKeys.Select(pp => $"t.{pp.PropertyName.ToSafeName()}")));
×
326
                CodeBuilder.Append(" }");
×
327
            }
328
            else
329
            {
330
                var propertyName = primaryKeys.First().PropertyName.ToSafeName();
×
331
                CodeBuilder.Append($"t.{propertyName}");
×
332
            }
333
            CodeBuilder.Append(")");
×
334
        }
335

336
        if (!string.IsNullOrEmpty(relationship.RelationshipName))
77!
337
        {
338
            CodeBuilder.AppendLine();
77✔
339
            CodeBuilder.Append(".HasConstraintName(\"");
77✔
340
            CodeBuilder.Append(relationship.RelationshipName);
77✔
341
            CodeBuilder.Append("\")");
77✔
342
        }
343

344
        CodeBuilder.DecrementIndent();
77✔
345

346
        CodeBuilder.AppendLine(";");
77✔
347
    }
77✔
348

349

350
    private void GeneratePropertyMapping()
351
    {
352
        CodeBuilder.AppendLine("// properties");
160✔
353
        foreach (var property in _entity.Properties)
2,880✔
354
        {
355
            GeneratePropertyMapping(property);
1,280✔
356
            CodeBuilder.AppendLine();
1,280✔
357
        }
358
    }
160✔
359

360
    private void GeneratePropertyMapping(Property property)
361
    {
362
        bool isString = property.SystemType == typeof(string);
1,280✔
363
        bool isByteArray = property.SystemType == typeof(byte[]);
1,280✔
364

365
        CodeBuilder.Append($"builder.Property(t => t.{property.PropertyName})");
1,280✔
366

367
        CodeBuilder.IncrementIndent();
1,280✔
368
        if (property.IsRequired)
1,280✔
369
        {
370
            CodeBuilder.AppendLine();
669✔
371
            CodeBuilder.Append(".IsRequired()");
669✔
372
        }
373

374
        if (property.IsRowVersion == true)
1,280✔
375
        {
376
            if (property.DataType == DbType.Binary && property.SystemType != typeof(byte[]))
26!
377
            {
378
                CodeBuilder.AppendLine();
×
379
                CodeBuilder.Append(".HasConversion<byte[]>()");
×
380
            }
381
            CodeBuilder.AppendLine();
26✔
382
            CodeBuilder.Append(".IsRowVersion()");
26✔
383
        }
384

385
        if (property.IsConcurrencyToken == true)
1,280✔
386
        {
387
            CodeBuilder.AppendLine();
26✔
388
            CodeBuilder.Append(".IsConcurrencyToken()");
26✔
389
        }
390

391
        CodeBuilder.AppendLine();
1,280✔
392
        CodeBuilder.Append($".HasColumnName({property.ColumnName.ToLiteral()})");
1,280✔
393

394
        if (!string.IsNullOrEmpty(property.NativeType))
1,280✔
395
        {
396
            CodeBuilder.AppendLine();
1,279✔
397
            CodeBuilder.Append($".HasColumnType({property.NativeType.ToLiteral()})");
1,279✔
398
        }
399

400
        if ((isString || isByteArray) && property.Size > 0)
1,280✔
401
        {
402
            CodeBuilder.AppendLine();
382✔
403
            CodeBuilder.Append($".HasMaxLength({property.Size.Value.ToString(CultureInfo.InvariantCulture)})");
382✔
404
        }
405

406
        // only use for simple types
407
        if (property.DefaultValue is bool or int or long or byte or double or float or short)
1,280!
408
        {
409
            CodeBuilder.AppendLine();
×
410
            CodeBuilder.Append($".HasDefaultValue({property.DefaultValue?.ToString()?.ToLowerInvariant()})");
×
411
        }
412
        else if (!string.IsNullOrEmpty(property.Default))
1,280✔
413
        {
414
            CodeBuilder.AppendLine();
142✔
415
            CodeBuilder.Append($".HasDefaultValueSql({property.Default.ToLiteral()})");
142✔
416
        }
417

418
        if (property.IsIdentity == true)
1,280✔
419
        {
420
            CodeBuilder.AppendLine();
65✔
421
            CodeBuilder.Append(".ValueGeneratedOnAdd()");
65✔
422
        }
423
        else if (property.IsComputed == true || property.IsRowVersion == true)
1,215✔
424
        {
425
            CodeBuilder.AppendLine();
44✔
426
            CodeBuilder.Append(".ValueGeneratedOnAddOrUpdate()");
44✔
427
        }
428

429
        CodeBuilder.DecrementIndent();
1,280✔
430

431
        CodeBuilder.AppendLine(";");
1,280✔
432
    }
1,280✔
433

434

435
    private void GenerateKeyMapping()
436
    {
437
        CodeBuilder.AppendLine("// key");
160✔
438

439
        var keys = _entity.Properties.Where(p => p.IsPrimaryKey == true).ToList();
160✔
440
        if (keys.Count == 0)
160✔
441
        {
442
            CodeBuilder.AppendLine("builder.HasNoKey();");
13✔
443
            CodeBuilder.AppendLine();
13✔
444

445
            return;
13✔
446
        }
447

448
        CodeBuilder.Append("builder.HasKey(t => ");
147✔
449

450
        if (keys.Count == 1)
147✔
451
        {
452
            var propertyName = keys[0].PropertyName.ToSafeName();
129✔
453
            CodeBuilder.AppendLine($"t.{propertyName});");
129✔
454
            CodeBuilder.AppendLine();
129✔
455

456
            return;
129✔
457
        }
458

459
        bool wroteLine = false;
18✔
460

461
        CodeBuilder.Append("new { ");
18✔
462
        foreach (var p in keys)
108✔
463
        {
464
            if (wroteLine)
36✔
465
                CodeBuilder.Append(", ");
18✔
466

467
            CodeBuilder.Append("t.");
36✔
468
            CodeBuilder.Append(p.PropertyName);
36✔
469
            wroteLine = true;
36✔
470
        }
471

472
        CodeBuilder.AppendLine(" });");
18✔
473
        CodeBuilder.AppendLine();
18✔
474
    }
18✔
475

476
    private void GenerateTableMapping()
477
    {
478
        CodeBuilder.AppendLine("// table");
160✔
479

480
        var method = _entity.IsView ? "ToView" : "ToTable";
160✔
481

482
        CodeBuilder.AppendLine(_entity.TableSchema.HasValue()
160✔
483
            ? $"builder.{method}(\"{_entity.TableName}\", \"{_entity.TableSchema}\");"
160✔
484
            : $"builder.{method}(\"{_entity.TableName}\");");
160✔
485

486
        CodeBuilder.AppendLine();
160✔
487

488
        if (_entity.TemporalTableName.IsNullOrEmpty())
160✔
489
            return;
159✔
490

491
        CodeBuilder.AppendLine("builder");
1✔
492
        CodeBuilder.IncrementIndent();
1✔
493
        CodeBuilder.AppendLine(".ToTable(tableBuilder => tableBuilder");
1✔
494
        CodeBuilder.IncrementIndent();
1✔
495
        CodeBuilder.AppendLine(".IsTemporal(temporalBuilder =>");
1✔
496
        CodeBuilder.AppendLine("{");
1✔
497
        CodeBuilder.IncrementIndent();
1✔
498
        CodeBuilder.AppendLine("temporalBuilder");
1✔
499
        CodeBuilder.IncrementIndent();
1✔
500

501
        CodeBuilder.AppendLine(_entity.TemporalTableSchema.HasValue()
1!
502
            ? $".UseHistoryTable(\"{_entity.TemporalTableName}\", \"{_entity.TemporalTableSchema}\");"
1✔
503
            : $".UseHistoryTable(\"{_entity.TemporalTableName}\");");
1✔
504

505
        CodeBuilder.DecrementIndent();
1✔
506
        CodeBuilder.AppendLine("temporalBuilder");
1✔
507
        CodeBuilder.IncrementIndent();
1✔
508
        CodeBuilder.AppendLine($".HasPeriodStart(\"{_entity.TemporalStartProperty}\")");
1✔
509
        CodeBuilder.AppendLine($".HasColumnName(\"{_entity.TemporalStartColumn}\");");
1✔
510

511
        CodeBuilder.DecrementIndent();
1✔
512
        CodeBuilder.AppendLine("temporalBuilder");
1✔
513
        CodeBuilder.IncrementIndent();
1✔
514
        CodeBuilder.AppendLine($".HasPeriodEnd(\"{_entity.TemporalEndProperty}\")");
1✔
515
        CodeBuilder.AppendLine($".HasColumnName(\"{_entity.TemporalEndColumn}\");");
1✔
516

517
        CodeBuilder.DecrementIndent();
1✔
518
        CodeBuilder.DecrementIndent();
1✔
519
        CodeBuilder.AppendLine("})");
1✔
520

521
        CodeBuilder.DecrementIndent();
1✔
522
        CodeBuilder.AppendLine(");");
1✔
523

524
        CodeBuilder.DecrementIndent();
1✔
525

526
        CodeBuilder.AppendLine();
1✔
527
    }
1✔
528
}
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