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

loresoft / EntityFrameworkCore.Generator / 15282827539

27 May 2025 06:24PM UTC coverage: 54.862% (-0.02%) from 54.881%
15282827539

push

github

web-flow
Merge pull request #575 from DavidBoone/574-nonPrimaryPrincipalKey

Generate HasPrincipalKey on relationship if necessary

623 of 1297 branches covered (48.03%)

Branch coverage included in aggregate %.

5 of 14 new or added lines in 1 file covered. (35.71%)

1854 of 3218 relevant lines covered (57.61%)

61.71 hits per line

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

73.91
/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
using Microsoft.EntityFrameworkCore;
9
using Microsoft.EntityFrameworkCore.Metadata;
10

11
namespace EntityFrameworkCore.Generator.Templates;
12

13
public class MappingClassTemplate : CodeTemplateBase
14
{
15
    private readonly Entity _entity;
16

17
    public MappingClassTemplate(Entity entity, GeneratorOptions options) : base(options)
22✔
18
    {
19
        _entity = entity;
22✔
20
    }
22✔
21

22

23
    public override string WriteCode()
24
    {
25
        CodeBuilder.Clear();
22✔
26

27
        CodeBuilder.AppendLine("using System;");
22✔
28
        CodeBuilder.AppendLine("using System.Collections.Generic;");
22✔
29
        CodeBuilder.AppendLine("using Microsoft.EntityFrameworkCore;");
22✔
30
        CodeBuilder.AppendLine();
22✔
31

32
        CodeBuilder.Append($"namespace {_entity.MappingNamespace}");
22✔
33

34
        if (Options.Project.FileScopedNamespace)
22!
35
        {
36
            CodeBuilder.AppendLine(";");
×
37
            CodeBuilder.AppendLine();
×
38
            GenerateClass();
×
39
        }
40
        else
41
        {
42
            CodeBuilder.AppendLine();
22✔
43
            CodeBuilder.AppendLine("{");
22✔
44

45
            using (CodeBuilder.Indent())
22✔
46
            {
47
                GenerateClass();
22✔
48
            }
22✔
49

50
            CodeBuilder.AppendLine("}");
22✔
51
        }
52

53
        return CodeBuilder.ToString();
22✔
54
    }
55

56

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

63
        if (Options.Data.Mapping.Document)
22!
64
        {
65
            CodeBuilder.AppendLine("/// <summary>");
×
66
            CodeBuilder.AppendLine($"/// Allows configuration for an entity type <see cref=\"{safeName}\" />");
×
67
            CodeBuilder.AppendLine("/// </summary>");
×
68
        }
69

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

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

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

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

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

85
    }
22✔
86

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

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

94
        CodeBuilder.AppendLine("public readonly struct Table");
22✔
95
        CodeBuilder.AppendLine("{");
22✔
96

97
        using (CodeBuilder.Indent())
22✔
98
        {
99

100
            if (Options.Data.Mapping.Document)
22!
101
                CodeBuilder.AppendLine($"/// <summary>Table Schema name constant for entity <see cref=\"{safeName}\" /></summary>");
×
102

103
            CodeBuilder.AppendLine($"public const string Schema = \"{_entity.TableSchema}\";");
22✔
104

105
            if (Options.Data.Mapping.Document)
22!
106
                CodeBuilder.AppendLine($"/// <summary>Table Name constant for entity <see cref=\"{safeName}\" /></summary>");
×
107

108
            CodeBuilder.AppendLine($"public const string Name = \"{_entity.TableName}\";");
22✔
109
        }
22✔
110

111
        CodeBuilder.AppendLine("}");
22✔
112

113
        CodeBuilder.AppendLine();
22✔
114
        CodeBuilder.AppendLine("public readonly struct Columns");
22✔
115
        CodeBuilder.AppendLine("{");
22✔
116

117
        using (CodeBuilder.Indent())
22✔
118
        {
119
            foreach (var property in _entity.Properties)
464✔
120
            {
121
                if (Options.Data.Mapping.Document)
210!
122
                    CodeBuilder.AppendLine($"/// <summary>Column Name constant for property <see cref=\"{safeName}.{property.PropertyName}\" /></summary>");
×
123

124
                CodeBuilder.AppendLine($"public const string {property.PropertyName.ToSafeName()} = {property.ColumnName.ToLiteral()};");
210✔
125
            }
126
        }
127

128
        CodeBuilder.AppendLine("}");
22✔
129
        CodeBuilder.AppendLine("#endregion");
22✔
130
    }
22✔
131

132
    private void GenerateConfigure()
133
    {
134
        var entityClass = _entity.EntityClass.ToSafeName();
22✔
135
        var entityFullName = $"{_entity.EntityNamespace}.{entityClass}";
22✔
136

137
        if (Options.Data.Mapping.Document)
22!
138
        {
139
            CodeBuilder.AppendLine("/// <summary>");
×
140
            CodeBuilder.AppendLine($"/// Configures the entity of type <see cref=\"{entityFullName}\" />");
×
141
            CodeBuilder.AppendLine("/// </summary>");
×
142
            CodeBuilder.AppendLine("/// <param name=\"builder\">The builder to be used to configure the entity type.</param>");
×
143
        }
144

145
        CodeBuilder.AppendLine($"public void Configure(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<{entityFullName}> builder)");
22✔
146
        CodeBuilder.AppendLine("{");
22✔
147

148
        using (CodeBuilder.Indent())
22✔
149
        {
150
            CodeBuilder.AppendLine("#region Generated Configure");
22✔
151

152
            GenerateTableMapping();
22✔
153
            GenerateKeyMapping();
22✔
154
            GeneratePropertyMapping();
22✔
155
            GenerateRelationshipMapping();
22✔
156

157
            CodeBuilder.AppendLine("#endregion");
22✔
158
        }
22✔
159

160
        CodeBuilder.AppendLine("}");
22✔
161
        CodeBuilder.AppendLine();
22✔
162
    }
22✔
163

164

165
    private void GenerateRelationshipMapping()
166
    {
167
        CodeBuilder.AppendLine("// relationships");
22✔
168
        foreach (var relationship in _entity.Relationships.Where(e => e.IsMapped))
100✔
169
        {
170
            GenerateRelationshipMapping(relationship);
14✔
171
            CodeBuilder.AppendLine();
14✔
172
        }
173

174
    }
22✔
175

176
    private void GenerateRelationshipMapping(Relationship relationship)
177
    {
178
        CodeBuilder.Append("builder.HasOne(t => t.");
14✔
179
        CodeBuilder.Append(relationship.PropertyName);
14✔
180
        CodeBuilder.Append(")");
14✔
181
        CodeBuilder.AppendLine();
14✔
182

183
        CodeBuilder.IncrementIndent();
14✔
184

185
        CodeBuilder.Append(relationship.PrimaryCardinality == Cardinality.Many
14✔
186
            ? ".WithMany(t => t."
14✔
187
            : ".WithOne(t => t.");
14✔
188

189
        CodeBuilder.Append(relationship.PrimaryPropertyName);
14✔
190
        CodeBuilder.Append(")");
14✔
191

192
        CodeBuilder.AppendLine();
14✔
193
        CodeBuilder.Append(".HasForeignKey");
14✔
194
        if (relationship.IsOneToOne)
14✔
195
        {
196
            CodeBuilder.Append("<");
2✔
197
            CodeBuilder.Append(_entity.EntityNamespace);
2✔
198
            CodeBuilder.Append(".");
2✔
199
            CodeBuilder.Append(_entity.EntityClass.ToSafeName());
2✔
200
            CodeBuilder.Append(">");
2✔
201
        }
202
        CodeBuilder.Append("(d => ");
14✔
203

204
        var keys = relationship.Properties;
14✔
205
        bool wroteLine = false;
14✔
206

207
        if (keys.Count == 1)
14!
208
        {
209
            var propertyName = keys[0].PropertyName.ToSafeName();
14✔
210
            CodeBuilder.Append($"d.{propertyName}");
14✔
211
        }
212
        else
213
        {
214
            CodeBuilder.Append("new { ");
×
215
            foreach (var p in keys)
×
216
            {
217
                if (wroteLine)
×
218
                    CodeBuilder.Append(", ");
×
219

220
                CodeBuilder.Append($"d.{p.PropertyName}");
×
221
                wroteLine = true;
×
222
            }
223
            CodeBuilder.Append("}");
×
224
        }
225
        CodeBuilder.Append(")");
14✔
226

227
        var primaryKeys = relationship.PrimaryProperties;
14✔
228
        var nonPrimaryPrincipalKey = !primaryKeys
14✔
229
            .Select(pp => relationship.PrimaryEntity.Properties.ByProperty(pp.PropertyName))
14✔
230
            .All(p => p.IsPrimaryKey ?? true);
28✔
231

232
        if (nonPrimaryPrincipalKey)
14!
233
        {
NEW
234
            CodeBuilder.AppendLine();
×
235

NEW
236
            CodeBuilder.Append(".HasPrincipalKey(t => ");
×
NEW
237
            if (primaryKeys.Count > 1)
×
238
            {
NEW
239
                CodeBuilder.Append("new { ");
×
NEW
240
                CodeBuilder.Append(string.Join(", ", primaryKeys.Select(pp => $"t.{pp.PropertyName.ToSafeName()}")));
×
NEW
241
                CodeBuilder.Append(" }");
×
242
            }
243
            else
244
            {
NEW
245
                var propertyName = primaryKeys.First().PropertyName.ToSafeName();
×
NEW
246
                CodeBuilder.Append($"t.{propertyName}");
×
247
            }
NEW
248
            CodeBuilder.Append(")");
×
249
        }
250

251
        if (!string.IsNullOrEmpty(relationship.RelationshipName))
14✔
252
        {
253
            CodeBuilder.AppendLine();
14✔
254
            CodeBuilder.Append(".HasConstraintName(\"");
14✔
255
            CodeBuilder.Append(relationship.RelationshipName);
14✔
256
            CodeBuilder.Append("\")");
14✔
257
        }
258

259
        CodeBuilder.DecrementIndent();
14✔
260

261
        CodeBuilder.AppendLine(";");
14✔
262
    }
14✔
263

264

265
    private void GeneratePropertyMapping()
266
    {
267
        CodeBuilder.AppendLine("// properties");
22✔
268
        foreach (var property in _entity.Properties)
464✔
269
        {
270
            GeneratePropertyMapping(property);
210✔
271
            CodeBuilder.AppendLine();
210✔
272
        }
273
    }
22✔
274

275
    private void GeneratePropertyMapping(Property property)
276
    {
277
        bool isString = property.SystemType == typeof(string);
210✔
278
        bool isByteArray = property.SystemType == typeof(byte[]);
210✔
279

280
        CodeBuilder.Append($"builder.Property(t => t.{property.PropertyName})");
210✔
281

282
        CodeBuilder.IncrementIndent();
210✔
283
        if (property.IsRequired)
210✔
284
        {
285
            CodeBuilder.AppendLine();
120✔
286
            CodeBuilder.Append(".IsRequired()");
120✔
287
        }
288

289
        if (property.IsRowVersion == true)
210✔
290
        {
291
            if (property.DataType == DbType.Binary && property.SystemType != typeof(byte[]))
16!
292
            {
293
                CodeBuilder.AppendLine();
×
294
                CodeBuilder.Append(".HasConversion<byte[]>()");
×
295
            }
296
            CodeBuilder.AppendLine();
16✔
297
            CodeBuilder.Append(".IsRowVersion()");
16✔
298
        }
299

300
        if (property.IsConcurrencyToken == true)
210✔
301
        {
302
            CodeBuilder.AppendLine();
16✔
303
            CodeBuilder.Append(".IsConcurrencyToken()");
16✔
304
        }
305

306
        CodeBuilder.AppendLine();
210✔
307
        CodeBuilder.Append($".HasColumnName({property.ColumnName.ToLiteral()})");
210✔
308

309
        if (!string.IsNullOrEmpty(property.StoreType))
210✔
310
        {
311
            CodeBuilder.AppendLine();
210✔
312
            CodeBuilder.Append($".HasColumnType({property.StoreType.ToLiteral()})");
210✔
313
        }
314

315
        if ((isString || isByteArray) && property.Size > 0)
210✔
316
        {
317
            CodeBuilder.AppendLine();
90✔
318
            CodeBuilder.Append($".HasMaxLength({property.Size.Value.ToString(CultureInfo.InvariantCulture)})");
90✔
319
        }
320

321
        // only use for simple types
322
        if (property.DefaultValue is bool or int or long or byte or double or float or short)
210✔
323
        {
324
            CodeBuilder.AppendLine();
18✔
325
            CodeBuilder.Append($".HasDefaultValue({property.DefaultValue?.ToString()?.ToLowerInvariant()})");
18!
326
        }
327
        else if (!string.IsNullOrEmpty(property.Default))
192✔
328
        {
329
            CodeBuilder.AppendLine();
32✔
330
            CodeBuilder.Append($".HasDefaultValueSql({property.Default.ToLiteral()})");
32✔
331
        }
332

333
        switch (property.ValueGenerated)
210!
334
        {
335
            case ValueGenerated.OnAdd:
336
                CodeBuilder.AppendLine();
6✔
337
                CodeBuilder.Append(".ValueGeneratedOnAdd()");
6✔
338
                break;
6✔
339
            case ValueGenerated.OnAddOrUpdate:
340
                CodeBuilder.AppendLine();
16✔
341
                CodeBuilder.Append(".ValueGeneratedOnAddOrUpdate()");
16✔
342
                break;
16✔
343
            case ValueGenerated.OnUpdate:
344
                CodeBuilder.AppendLine();
×
345
                CodeBuilder.Append(".ValueGeneratedOnUpdate()");
×
346
                break;
347
        }
348
        CodeBuilder.DecrementIndent();
210✔
349

350
        CodeBuilder.AppendLine(";");
210✔
351
    }
210✔
352

353

354
    private void GenerateKeyMapping()
355
    {
356
        CodeBuilder.AppendLine("// key");
22✔
357

358
        var keys = _entity.Properties.Where(p => p.IsPrimaryKey == true).ToList();
232✔
359
        if (keys.Count == 0)
22!
360
        {
361
            CodeBuilder.AppendLine("builder.HasNoKey();");
×
362
            CodeBuilder.AppendLine();
×
363

364
            return;
×
365
        }
366

367
        CodeBuilder.Append("builder.HasKey(t => ");
22✔
368

369
        if (keys.Count == 1)
22✔
370
        {
371
            var propertyName = keys[0].PropertyName.ToSafeName();
20✔
372
            CodeBuilder.AppendLine($"t.{propertyName});");
20✔
373
            CodeBuilder.AppendLine();
20✔
374

375
            return;
20✔
376
        }
377

378
        bool wroteLine = false;
2✔
379

380
        CodeBuilder.Append("new { ");
2✔
381
        foreach (var p in keys)
12✔
382
        {
383
            if (wroteLine)
4✔
384
                CodeBuilder.Append(", ");
2✔
385

386
            CodeBuilder.Append("t.");
4✔
387
            CodeBuilder.Append(p.PropertyName);
4✔
388
            wroteLine = true;
4✔
389
        }
390

391
        CodeBuilder.AppendLine(" });");
2✔
392
        CodeBuilder.AppendLine();
2✔
393
    }
2✔
394

395
    private void GenerateTableMapping()
396
    {
397
        CodeBuilder.AppendLine("// table");
22✔
398

399
        var method = _entity.IsView
22!
400
            ? nameof(RelationalEntityTypeBuilderExtensions.ToView)
22✔
401
            : nameof(RelationalEntityTypeBuilderExtensions.ToTable);
22✔
402

403
        CodeBuilder.AppendLine(_entity.TableSchema.HasValue()
22!
404
            ? $"builder.{method}(\"{_entity.TableName}\", \"{_entity.TableSchema}\");"
22✔
405
            : $"builder.{method}(\"{_entity.TableName}\");");
22✔
406

407
        CodeBuilder.AppendLine();
22✔
408

409
        if (_entity.TemporalTableName.IsNullOrEmpty())
22!
410
            return;
22✔
411

412
        CodeBuilder.AppendLine("builder");
×
413
        CodeBuilder.IncrementIndent();
×
414
        CodeBuilder.AppendLine(".ToTable(tableBuilder => tableBuilder");
×
415
        CodeBuilder.IncrementIndent();
×
416
        CodeBuilder.AppendLine(".IsTemporal(temporalBuilder =>");
×
417
        CodeBuilder.AppendLine("{");
×
418
        CodeBuilder.IncrementIndent();
×
419
        CodeBuilder.AppendLine("temporalBuilder");
×
420
        CodeBuilder.IncrementIndent();
×
421

422
        CodeBuilder.AppendLine(_entity.TemporalTableSchema.HasValue()
×
423
            ? $".UseHistoryTable(\"{_entity.TemporalTableName}\", \"{_entity.TemporalTableSchema}\");"
×
424
            : $".UseHistoryTable(\"{_entity.TemporalTableName}\");");
×
425

426
        CodeBuilder.DecrementIndent();
×
427
        CodeBuilder.AppendLine("temporalBuilder");
×
428
        CodeBuilder.IncrementIndent();
×
429
        CodeBuilder.AppendLine($".HasPeriodStart(\"{_entity.TemporalStartProperty}\")");
×
430
        CodeBuilder.AppendLine($".HasColumnName(\"{_entity.TemporalStartColumn}\");");
×
431

432
        CodeBuilder.DecrementIndent();
×
433
        CodeBuilder.AppendLine("temporalBuilder");
×
434
        CodeBuilder.IncrementIndent();
×
435
        CodeBuilder.AppendLine($".HasPeriodEnd(\"{_entity.TemporalEndProperty}\")");
×
436
        CodeBuilder.AppendLine($".HasColumnName(\"{_entity.TemporalEndColumn}\");");
×
437

438
        CodeBuilder.DecrementIndent();
×
439
        CodeBuilder.DecrementIndent();
×
440
        CodeBuilder.AppendLine("})");
×
441

442
        CodeBuilder.DecrementIndent();
×
443
        CodeBuilder.AppendLine(");");
×
444

445
        CodeBuilder.DecrementIndent();
×
446

447
        CodeBuilder.AppendLine();
×
448
    }
×
449
}
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