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

loresoft / EntityFrameworkCore.Generator / 15075133234

16 May 2025 06:36PM UTC coverage: 54.934% (-0.5%) from 55.392%
15075133234

push

github

web-flow
Merge pull request #620 from DavidBoone/parseCompositeForeignKeys

Add support for composite foreign keys to Parsing/MappingVisitor

618 of 1281 branches covered (48.24%)

Branch coverage included in aggregate %.

5 of 37 new or added lines in 1 file covered. (13.51%)

1826 of 3168 relevant lines covered (57.64%)

87.62 hits per line

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

71.6
/src/EntityFrameworkCore.Generator.Core/Parsing/MappingVisitor.cs
1
using EntityFrameworkCore.Generator.Metadata.Parsing;
2

3
using Microsoft.CodeAnalysis.CSharp;
4
using Microsoft.CodeAnalysis.CSharp.Syntax;
5

6
namespace EntityFrameworkCore.Generator.Parsing;
7

8
public class MappingVisitor : CSharpSyntaxWalker
9
{
10
    private ParsedProperty? _currentProperty;
11
    private ParsedRelationship? _currentRelationship;
12

13

14
    public MappingVisitor()
22✔
15
    {
16
        MappingBaseType = "IEntityTypeConfiguration";
22✔
17
    }
22✔
18

19
    public string MappingBaseType { get; set; }
44✔
20

21
    public ParsedEntity? ParsedEntity { get; set; }
872✔
22

23

24
    public override void VisitClassDeclaration(ClassDeclarationSyntax node)
25
    {
26
        ParseClassNames(node);
22✔
27
        base.VisitClassDeclaration(node);
22✔
28
    }
22✔
29

30
    public override void VisitInvocationExpression(InvocationExpressionSyntax node)
31
    {
32

33
        var methodName = ParseMethodName(node);
1,044!
34

35
        switch (methodName)
36
        {
37
            case "HasConstraintName":
38
                ParseConstraintName(node);
14✔
39
                break;
14✔
40
            case "HasForeignKey":
41
                ParseForeignKey(node);
12✔
42
                break;
12✔
43
            case "WithMany":
44
            case "WithOne":
45
                ParseWithMany(node);
14✔
46
                break;
14✔
47
            case "HasMany":
48
            case "HasOne":
49
                ParseHasOne(node);
14✔
50
                break;
14✔
51
            case "HasColumnName":
52
                ParseColumnName(node);
210✔
53
                break;
210✔
54
            case "Property":
55
                ParseProperty(node);
210✔
56
                break;
210✔
57
            case "ToTable":
58
            case "ToView":
59
                ParseTable(node);
22✔
60
                break;
61
        }
62

63
        base.VisitInvocationExpression(node);
1,044✔
64
    }
1,044✔
65

66

67
    private static string ParseMethodName(InvocationExpressionSyntax node)
68
    {
69
        var memberAccess = node
1,044✔
70
            .ChildNodes()
1,044✔
71
            .OfType<MemberAccessExpressionSyntax>()
1,044✔
72
            .FirstOrDefault();
1,044✔
73

74
        if (memberAccess == null)
1,044!
75
            return string.Empty;
×
76

77
        var methodName = memberAccess
1,044✔
78
            .ChildNodes()
1,044✔
79
            .OfType<IdentifierNameSyntax>()
1,044✔
80
            .Select(s => s.Identifier.ValueText)
1,310✔
81
            .LastOrDefault();
1,044✔
82

83
        return methodName ?? string.Empty;
1,044✔
84
    }
85

86
    private static string? ParseLambaExpression(InvocationExpressionSyntax node)
87
    {
88
        if (node == null)
250!
89
            return null;
×
90

91
        var lambaExpression = node
250✔
92
            .ArgumentList
250✔
93
            .DescendantNodes()
250✔
94
            .OfType<LambdaExpressionSyntax>()
250✔
95
            .FirstOrDefault();
250✔
96

97
        if (lambaExpression == null)
250!
98
            return null;
×
99

100
        var simpleExpression = lambaExpression
250✔
101
            .ChildNodes()
250✔
102
            .OfType<MemberAccessExpressionSyntax>()
250✔
103
            .FirstOrDefault();
250✔
104

105
        if (simpleExpression == null)
250!
106
            return null;
×
107

108
        var propertyName = simpleExpression
250✔
109
            .ChildNodes()
250✔
110
            .OfType<IdentifierNameSyntax>()
250✔
111
            .Select(s => s.Identifier.ValueText)
500✔
112
            .LastOrDefault();
250✔
113

114
        return propertyName;
250✔
115
    }
116

117
    private List<string> ParseLambdaExpressionForAnonymousObject(InvocationExpressionSyntax node)
118
    {
NEW
119
        if (node == null)
×
NEW
120
            return null;
×
121

NEW
122
        var lambdaExpression = node
×
NEW
123
            .ArgumentList
×
NEW
124
            .DescendantNodes()
×
NEW
125
            .OfType<LambdaExpressionSyntax>()
×
NEW
126
            .FirstOrDefault();
×
127

NEW
128
        if (lambdaExpression == null)
×
NEW
129
            return null;
×
130

NEW
131
        var anonymousObject = lambdaExpression
×
NEW
132
            .ChildNodes()
×
NEW
133
            .OfType<AnonymousObjectCreationExpressionSyntax>()
×
NEW
134
            .FirstOrDefault();
×
135

NEW
136
        if (anonymousObject == null)
×
NEW
137
            return null;
×
138

NEW
139
        var propertyNames = anonymousObject
×
NEW
140
            .ChildNodes()
×
NEW
141
            .OfType<AnonymousObjectMemberDeclaratorSyntax>()
×
NEW
142
            .Select(declarator => declarator
×
NEW
143
                .ChildNodes()
×
NEW
144
                .OfType<MemberAccessExpressionSyntax>()
×
NEW
145
                .FirstOrDefault())
×
NEW
146
            .Where(memberAccess => memberAccess != null)
×
NEW
147
            .Select(memberAccess => memberAccess
×
NEW
148
                .ChildNodes()
×
NEW
149
                .OfType<IdentifierNameSyntax>()
×
NEW
150
                .Select(identifier => identifier.Identifier.ValueText)
×
NEW
151
                .LastOrDefault())
×
NEW
152
            .Where(propertyName => propertyName != null)
×
NEW
153
            .ToList();
×
154

NEW
155
        return propertyNames;
×
156
    }
157

158
    private void ParseHasOne(InvocationExpressionSyntax node)
159
    {
160
        if (node == null || ParsedEntity == null)
14!
161
            return;
×
162

163
        _currentRelationship ??= new ParsedRelationship();
14!
164

165
        var propertyName = ParseLambaExpression(node);
14✔
166
        if (!string.IsNullOrEmpty(propertyName))
14✔
167
            _currentRelationship.PropertyName = propertyName;
14✔
168

169
        // add and reset current relationship
170
        if (_currentRelationship.IsValid())
14✔
171
            ParsedEntity.Relationships.Add(_currentRelationship);
12✔
172

173
        _currentRelationship = null;
14✔
174
    }
14✔
175

176
    private void ParseWithMany(InvocationExpressionSyntax node)
177
    {
178
        if (node == null || ParsedEntity == null)
14!
179
            return;
×
180

181
        var propertyName = ParseLambaExpression(node);
14✔
182
        if (string.IsNullOrEmpty(propertyName))
14!
183
            return;
×
184

185
        _currentRelationship ??= new ParsedRelationship();
14!
186
        _currentRelationship.PrimaryPropertyName = propertyName;
14✔
187
    }
14✔
188

189
    private void ParseForeignKey(InvocationExpressionSyntax node)
190
    {
191
        if (node == null || ParsedEntity == null)
12!
192
            return;
×
193

194
        List<string> propertyNames = null;
12✔
195
        if (ParseLambaExpression(node) is string propertyName && !string.IsNullOrEmpty(propertyName))
12!
196
            propertyNames = new List<string> { propertyName };
12✔
197
        else
NEW
198
            propertyNames = ParseLambdaExpressionForAnonymousObject(node);
×
199

200
        if (propertyNames == null)
12!
201
            return;
×
202

203
        _currentRelationship ??= new ParsedRelationship();
12!
204
        _currentRelationship.Properties.AddRange(propertyNames);
12✔
205
    }
12✔
206

207
    private void ParseConstraintName(InvocationExpressionSyntax node)
208
    {
209
        if (node == null || ParsedEntity == null)
14!
210
            return;
×
211

212
        var constraitName = node
14✔
213
            .ArgumentList
14✔
214
            .DescendantNodes()
14✔
215
            .OfType<LiteralExpressionSyntax>()
14✔
216
            .Select(t => t.Token.ValueText)
14✔
217
            .FirstOrDefault();
14✔
218

219
        _currentRelationship ??= new ParsedRelationship();
14✔
220
        _currentRelationship.RelationshipName = constraitName;
14✔
221
    }
14✔
222

223

224
    private void ParseProperty(InvocationExpressionSyntax node)
225
    {
226
        if (node == null || _currentProperty == null || ParsedEntity == null)
210!
227
            return;
×
228

229
        var propertyName = ParseLambaExpression(node);
210✔
230
        if (!string.IsNullOrEmpty(propertyName))
210✔
231
            _currentProperty.PropertyName = propertyName;
210✔
232

233
        // add and reset current property
234
        if (_currentProperty.IsValid())
210✔
235
            ParsedEntity.Properties.Add(_currentProperty);
210✔
236

237
        _currentProperty = null;
210✔
238
    }
210✔
239

240
    private void ParseColumnName(InvocationExpressionSyntax node)
241
    {
242
        if (node == null || ParsedEntity == null)
210!
243
            return;
×
244

245
        var columnName = node
210✔
246
            .ArgumentList
210✔
247
            .DescendantNodes()
210✔
248
            .OfType<LiteralExpressionSyntax>()
210✔
249
            .Select(t => t.Token.ValueText)
210✔
250
            .FirstOrDefault();
210✔
251

252
        if (string.IsNullOrEmpty(columnName))
210!
253
            return;
×
254

255
        _currentProperty = new ParsedProperty { ColumnName = columnName };
210✔
256
    }
210✔
257

258
    private void ParseTable(InvocationExpressionSyntax node)
259
    {
260
        if (node == null || ParsedEntity == null)
22!
261
            return;
×
262

263
        var arguments = node
22✔
264
            .ArgumentList
22✔
265
            .DescendantNodes()
22✔
266
            .OfType<LiteralExpressionSyntax>()
22✔
267
            .Select(t => t.Token.ValueText)
44✔
268
            .ToList();
22✔
269

270
        if (arguments.Count == 0)
22!
271
            return;
×
272

273
        if (arguments.Count >= 1)
22✔
274
            ParsedEntity.TableName = arguments[0];
22✔
275

276
        if (arguments.Count >= 2)
22✔
277
            ParsedEntity.TableSchema = arguments[1];
22✔
278
    }
22✔
279

280
    private void ParseClassNames(ClassDeclarationSyntax node)
281
    {
282
        if (node == null)
22!
283
            return;
×
284

285
        var baseType = node.BaseList
22!
286
            ?.DescendantNodes()
22✔
287
            .OfType<GenericNameSyntax>()
22✔
288
            .FirstOrDefault(t => t.Identifier.ValueText == MappingBaseType);
44✔
289

290
        if (baseType == null)
22!
291
            return;
×
292

293
        var firstArgument = baseType
22✔
294
            .TypeArgumentList
22✔
295
            .Arguments
22✔
296
            .FirstOrDefault();
22✔
297

298
        if (firstArgument == null)
22!
299
            return;
×
300

301
        // last identifier is class name
302
        var entityClass = firstArgument
22✔
303
            .DescendantNodesAndSelf()
22✔
304
            .OfType<IdentifierNameSyntax>()
22✔
305
            .Select(s => s.Identifier.ValueText)
88✔
306
            .LastOrDefault();
22✔
307

308
        var mappingClass = node.Identifier.Text;
22✔
309

310
        if (string.IsNullOrEmpty(entityClass) || string.IsNullOrEmpty(mappingClass))
22!
311
            return;
×
312

313
        if (ParsedEntity == null)
22✔
314
            ParsedEntity = new ParsedEntity();
22✔
315

316
        ParsedEntity.MappingClass = mappingClass;
22✔
317
        ParsedEntity.EntityClass = entityClass;
22✔
318
    }
22✔
319
}
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