@@ -29,8 +29,10 @@ public sealed class DefaultModelTypeBuilder : IModelTypeBuilder
29
29
static readonly Type IEnumerableOfT = typeof ( IEnumerable < > ) ;
30
30
readonly ICollection < Assembly > assemblies ;
31
31
readonly ConcurrentDictionary < ApiVersion , ModuleBuilder > modules = new ConcurrentDictionary < ApiVersion , ModuleBuilder > ( ) ;
32
- readonly ConcurrentDictionary < ClassSignature , Type > generatedTypes = new ConcurrentDictionary < ClassSignature , Type > ( ) ;
33
- readonly Dictionary < EdmTypeKey , Type > visitedEdmTypes = new Dictionary < EdmTypeKey , Type > ( ) ;
32
+ readonly ConcurrentDictionary < EdmTypeKey , Type > generatedEdmTypes = new ConcurrentDictionary < EdmTypeKey , Type > ( ) ;
33
+ readonly ConcurrentDictionary < EdmTypeKey , TypeBuilder > unfinishedTypes = new ConcurrentDictionary < EdmTypeKey , TypeBuilder > ( ) ;
34
+ readonly HashSet < EdmTypeKey > visitedEdmTypes = new HashSet < EdmTypeKey > ( ) ;
35
+ readonly Dictionary < EdmTypeKey , List < PropertyDependency > > dependencies = new Dictionary < EdmTypeKey , List < PropertyDependency > > ( ) ;
34
36
35
37
/// <summary>
36
38
/// Initializes a new instance of the <see cref="DefaultModelTypeBuilder"/> class.
@@ -52,16 +54,20 @@ public Type NewStructuredType( IEdmStructuredType structuredType, Type clrType,
52
54
53
55
var typeKey = new EdmTypeKey ( structuredType , apiVersion ) ;
54
56
55
- if ( visitedEdmTypes . TryGetValue ( typeKey , out var generatedType ) )
57
+ if ( generatedEdmTypes . TryGetValue ( typeKey , out var generatedType ) )
56
58
{
57
59
return generatedType ;
58
60
}
59
61
62
+ visitedEdmTypes . Add ( typeKey ) ;
63
+
60
64
const BindingFlags bindingFlags = BindingFlags . Public | BindingFlags . Instance ;
61
65
62
66
var properties = new List < ClassProperty > ( ) ;
63
67
var structuralProperties = structuredType . Properties ( ) . ToDictionary ( p => p . Name , StringComparer . OrdinalIgnoreCase ) ;
64
68
var clrTypeMatchesEdmType = true ;
69
+ var hasUnfinishedTypes = false ;
70
+ var dependentProperties = new Dictionary < string , Tuple < EdmTypeKey , bool > > ( ) ;
65
71
66
72
foreach ( var property in clrType . GetProperties ( bindingFlags ) )
67
73
{
@@ -73,14 +79,61 @@ public Type NewStructuredType( IEdmStructuredType structuredType, Type clrType,
73
79
74
80
var structuredTypeRef = structuralProperty . Type ;
75
81
var propertyType = property . PropertyType ;
82
+ var propertyTypeKey = new EdmTypeKey ( structuredTypeRef , apiVersion ) ;
76
83
77
84
if ( structuredTypeRef . IsCollection ( ) )
78
85
{
79
- propertyType = NewStructuredTypeOrSelf ( typeKey , structuredTypeRef . AsCollection ( ) , propertyType , apiVersion ) ;
86
+ var collectionType = structuredTypeRef . AsCollection ( ) ;
87
+ var elementType = collectionType . ElementType ( ) ;
88
+
89
+ if ( elementType . IsStructured ( ) )
90
+ {
91
+ assemblies . Add ( clrType . Assembly ) ;
92
+ visitedEdmTypes . Add ( propertyTypeKey ) ;
93
+
94
+ var itemType = elementType . Definition . GetClrType ( assemblies ) ;
95
+ var elementKey = new EdmTypeKey ( elementType , apiVersion ) ;
96
+
97
+ if ( visitedEdmTypes . Contains ( elementKey ) )
98
+ {
99
+ clrTypeMatchesEdmType = false ;
100
+ hasUnfinishedTypes = true ;
101
+ var keyTuple = new Tuple < EdmTypeKey , bool > ( elementKey , true ) ;
102
+ dependentProperties . Add ( property . Name , keyTuple ) ;
103
+ continue ;
104
+ }
105
+
106
+ var newItemType = NewStructuredType ( elementType . ToStructuredType ( ) , itemType , apiVersion ) ;
107
+
108
+ if ( newItemType is TypeBuilder )
109
+ {
110
+ hasUnfinishedTypes = true ;
111
+ }
112
+
113
+ if ( ! itemType . Equals ( newItemType ) )
114
+ {
115
+ propertyType = IEnumerableOfT . MakeGenericType ( newItemType ) ;
116
+ }
117
+ }
80
118
}
81
119
else if ( structuredTypeRef . IsStructured ( ) )
82
120
{
83
- propertyType = NewStructuredTypeOrSelf ( typeKey , structuredTypeRef . ToStructuredType ( ) , propertyType , apiVersion ) ;
121
+ if ( ! visitedEdmTypes . Contains ( propertyTypeKey ) )
122
+ {
123
+ propertyType = NewStructuredType ( structuredTypeRef . ToStructuredType ( ) , propertyType , apiVersion ) ;
124
+ if ( propertyType is TypeBuilder )
125
+ {
126
+ hasUnfinishedTypes = true ;
127
+ }
128
+ }
129
+ else
130
+ {
131
+ clrTypeMatchesEdmType = false ;
132
+ hasUnfinishedTypes = true ;
133
+ var keyTuple = new Tuple < EdmTypeKey , bool > ( propertyTypeKey , false ) ;
134
+ dependentProperties . Add ( property . Name , keyTuple ) ;
135
+ continue ;
136
+ }
84
137
}
85
138
86
139
clrTypeMatchesEdmType &= propertyType . IsDeclaringType ( ) || property . PropertyType . Equals ( propertyType ) ;
@@ -94,10 +147,28 @@ public Type NewStructuredType( IEdmStructuredType structuredType, Type clrType,
94
147
95
148
var signature = new ClassSignature ( clrType . FullName , properties , apiVersion ) ;
96
149
97
- generatedType = generatedTypes . GetOrAdd ( signature , CreateFromSignature ) ;
98
- visitedEdmTypes . Add ( typeKey , generatedType ) ;
150
+ if ( hasUnfinishedTypes )
151
+ {
152
+ if ( ! unfinishedTypes . TryGetValue ( typeKey , out var typeBuilder ) )
153
+ {
154
+ typeBuilder = CreateTypeBuilderFromSignature ( signature ) ;
155
+ var newPropertyDependencies = new List < PropertyDependency > ( ) ;
156
+ foreach ( var name in dependentProperties . Keys )
157
+ {
158
+ var keyTuple = dependentProperties [ name ] ;
159
+ newPropertyDependencies . Add ( new PropertyDependency ( typeBuilder , keyTuple . Item1 , name , keyTuple . Item2 ) ) ;
160
+ }
161
+
162
+ dependencies . Add ( typeKey , newPropertyDependencies ) ;
163
+ unfinishedTypes . GetOrAdd ( typeKey , typeBuilder ) ;
164
+
165
+ return ResolveDependencies ( typeBuilder , typeKey ) ;
166
+ }
99
167
100
- return generatedType ;
168
+ return typeBuilder ;
169
+ }
170
+
171
+ return generatedEdmTypes . GetOrAdd ( typeKey , CreateTypeInfoFromSignature ( signature ) ) ;
101
172
}
102
173
103
174
/// <inheritdoc />
@@ -111,97 +182,107 @@ public Type NewActionParameters( IEdmAction action, ApiVersion apiVersion )
111
182
var properties = action . Parameters . Where ( p => p . Name != "bindingParameter" ) . Select ( p => new ClassProperty ( assemblies , p ) ) ;
112
183
var signature = new ClassSignature ( name , properties , apiVersion ) ;
113
184
114
- return generatedTypes . GetOrAdd ( signature , CreateFromSignature ) ;
185
+ return CreateTypeInfoFromSignature ( signature ) ;
115
186
}
116
187
117
- TypeInfo CreateFromSignature ( ClassSignature @class )
188
+ TypeInfo CreateTypeInfoFromSignature ( ClassSignature @class )
118
189
{
119
190
Contract . Requires ( @class != null ) ;
120
191
Contract . Ensures ( Contract . Result < TypeInfo > ( ) != null ) ;
121
192
122
- const MethodAttributes PropertyMethodAttributes = MethodAttributes . Public | MethodAttributes . SpecialName | MethodAttributes . HideBySig ;
193
+ return CreateTypeBuilderFromSignature ( @class ) . CreateTypeInfo ( ) ;
194
+ }
195
+
196
+ TypeBuilder CreateTypeBuilderFromSignature ( ClassSignature @class )
197
+ {
198
+ Contract . Requires ( @class != null ) ;
199
+ Contract . Ensures ( Contract . Result < TypeBuilder > ( ) != null ) ;
200
+
123
201
var moduleBuilder = modules . GetOrAdd ( @class . ApiVersion , CreateModuleForApiVersion ) ;
124
202
var typeBuilder = moduleBuilder . DefineType ( @class . Name , TypeAttributes . Class ) ;
125
203
126
204
foreach ( var property in @class . Properties )
127
205
{
128
206
var type = property . GetType ( typeBuilder ) ;
129
207
var name = property . Name ;
130
- var field = typeBuilder . DefineField ( "_" + name , type , FieldAttributes . Private ) ;
131
- var propertyBuilder = typeBuilder . DefineProperty ( name , PropertyAttributes . HasDefault , type , null ) ;
132
- var getter = typeBuilder . DefineMethod ( "get_" + name , PropertyMethodAttributes , type , Type . EmptyTypes ) ;
133
- var setter = typeBuilder . DefineMethod ( "set_" + name , PropertyMethodAttributes , null , new [ ] { type } ) ;
134
- var il = getter . GetILGenerator ( ) ;
135
-
136
- il . Emit ( OpCodes . Ldarg_0 ) ;
137
- il . Emit ( OpCodes . Ldfld , field ) ;
138
- il . Emit ( OpCodes . Ret ) ;
139
-
140
- il = setter . GetILGenerator ( ) ;
141
- il . Emit ( OpCodes . Ldarg_0 ) ;
142
- il . Emit ( OpCodes . Ldarg_1 ) ;
143
- il . Emit ( OpCodes . Stfld , field ) ;
144
- il . Emit ( OpCodes . Ret ) ;
145
-
146
- propertyBuilder . SetGetMethod ( getter ) ;
147
- propertyBuilder . SetSetMethod ( setter ) ;
208
+ var propertyBuilder = AddProperty ( typeBuilder , type , name ) ;
148
209
149
210
foreach ( var attribute in property . Attributes )
150
211
{
151
212
propertyBuilder . SetCustomAttribute ( attribute ) ;
152
213
}
153
214
}
154
215
155
- return typeBuilder . CreateTypeInfo ( ) ;
216
+ return typeBuilder ;
156
217
}
157
218
158
- Type NewStructuredTypeOrSelf ( EdmTypeKey declaringTypeKey , IEdmCollectionTypeReference collectionType , Type clrType , ApiVersion apiVersion )
219
+ private Type ResolveDependencies ( TypeBuilder typeBuilder , EdmTypeKey typeKey )
159
220
{
160
- Contract . Requires ( collectionType != null ) ;
161
- Contract . Requires ( clrType != null ) ;
162
- Contract . Requires ( apiVersion != null ) ;
163
- Contract . Ensures ( Contract . Result < Type > ( ) != null ) ;
164
-
165
- var elementType = collectionType . ElementType ( ) ;
166
-
167
- if ( ! elementType . IsStructured ( ) )
168
- {
169
- return clrType ;
170
- }
221
+ var keys = dependencies . Keys . ToList ( ) ;
171
222
172
- var structuredType = elementType . ToStructuredType ( ) ;
173
-
174
- if ( declaringTypeKey == new EdmTypeKey ( structuredType , apiVersion ) )
223
+ foreach ( var key in keys )
175
224
{
176
- return IEnumerableOfT . MakeGenericType ( DeclaringType . Value ) ;
177
- }
178
-
179
- assemblies . Add ( clrType . Assembly ) ;
180
-
181
- var itemType = elementType . Definition . GetClrType ( assemblies ) ;
182
- var newItemType = NewStructuredType ( structuredType , itemType , apiVersion ) ;
225
+ var propertyDependencies = dependencies [ key ] ;
226
+ for ( var x = propertyDependencies . Count - 1 ; x >= 0 ; x -- )
227
+ {
228
+ var propertyDependency = propertyDependencies [ x ] ;
229
+ if ( propertyDependency . DependentOnTypeKey == typeKey )
230
+ {
231
+ if ( propertyDependency . IsCollection )
232
+ {
233
+ var collectionType = IEnumerableOfT . MakeGenericType ( typeBuilder ) ;
234
+ AddProperty ( propertyDependency . DependentType , collectionType , propertyDependency . PropertyName ) ;
235
+ }
236
+ else
237
+ {
238
+ AddProperty ( propertyDependency . DependentType , typeBuilder , propertyDependency . PropertyName ) ;
239
+ }
240
+
241
+ propertyDependencies . Remove ( propertyDependency ) ;
242
+ }
243
+ }
183
244
184
- if ( ! itemType . Equals ( newItemType ) )
185
- {
186
- clrType = IEnumerableOfT . MakeGenericType ( newItemType ) ;
245
+ if ( propertyDependencies . Count == 0 )
246
+ {
247
+ dependencies . Remove ( key ) ;
248
+ if ( unfinishedTypes . TryRemove ( key , out var type ) )
249
+ {
250
+ var typeInfo = type . CreateTypeInfo ( ) ;
251
+ generatedEdmTypes . GetOrAdd ( key , typeInfo ) ;
252
+
253
+ if ( key == typeKey )
254
+ {
255
+ return typeInfo ;
256
+ }
257
+ }
258
+ }
187
259
}
188
260
189
- return clrType ;
261
+ return typeBuilder ;
190
262
}
191
263
192
- Type NewStructuredTypeOrSelf ( EdmTypeKey declaringTypeKey , IEdmStructuredType structuredType , Type clrType , ApiVersion apiVersion )
264
+ static PropertyBuilder AddProperty ( TypeBuilder addTo , Type shouldBeAdded , string name )
193
265
{
194
- Contract . Requires ( structuredType != null ) ;
195
- Contract . Requires ( clrType != null ) ;
196
- Contract . Requires ( apiVersion != null ) ;
197
- Contract . Ensures ( Contract . Result < Type > ( ) != null ) ;
198
-
199
- if ( declaringTypeKey == new EdmTypeKey ( structuredType , apiVersion ) )
200
- {
201
- return DeclaringType . Value ;
202
- }
203
-
204
- return NewStructuredType ( structuredType , clrType , apiVersion ) ;
266
+ const MethodAttributes propertyMethodAttributes = MethodAttributes . Public | MethodAttributes . SpecialName | MethodAttributes . HideBySig ;
267
+ var field = addTo . DefineField ( name , shouldBeAdded , FieldAttributes . Private ) ;
268
+ var propertyBuilder = addTo . DefineProperty ( name , PropertyAttributes . HasDefault , shouldBeAdded , null ) ;
269
+ var getter = addTo . DefineMethod ( "get_" + name , propertyMethodAttributes , shouldBeAdded , Type . EmptyTypes ) ;
270
+ var setter = addTo . DefineMethod ( "set_" + name , propertyMethodAttributes , shouldBeAdded , Type . EmptyTypes ) ;
271
+ var il = getter . GetILGenerator ( ) ;
272
+
273
+ il . Emit ( OpCodes . Ldarg_0 ) ;
274
+ il . Emit ( OpCodes . Ldfld , field ) ;
275
+ il . Emit ( OpCodes . Ret ) ;
276
+
277
+ il = setter . GetILGenerator ( ) ;
278
+ il . Emit ( OpCodes . Ldarg_0 ) ;
279
+ il . Emit ( OpCodes . Ldarg_1 ) ;
280
+ il . Emit ( OpCodes . Stfld , field ) ;
281
+ il . Emit ( OpCodes . Ret ) ;
282
+ propertyBuilder . SetGetMethod ( getter ) ;
283
+ propertyBuilder . SetSetMethod ( setter ) ;
284
+
285
+ return propertyBuilder ;
205
286
}
206
287
207
288
static ModuleBuilder CreateModuleForApiVersion ( ApiVersion apiVersion )
0 commit comments