@@ -29,8 +29,10 @@ public sealed class DefaultModelTypeBuilder : IModelTypeBuilder
2929 static readonly Type IEnumerableOfT = typeof ( IEnumerable < > ) ;
3030 readonly ICollection < Assembly > assemblies ;
3131 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 > > ( ) ;
3436
3537 /// <summary>
3638 /// Initializes a new instance of the <see cref="DefaultModelTypeBuilder"/> class.
@@ -52,16 +54,20 @@ public Type NewStructuredType( IEdmStructuredType structuredType, Type clrType,
5254
5355 var typeKey = new EdmTypeKey ( structuredType , apiVersion ) ;
5456
55- if ( visitedEdmTypes . TryGetValue ( typeKey , out var generatedType ) )
57+ if ( generatedEdmTypes . TryGetValue ( typeKey , out var generatedType ) )
5658 {
5759 return generatedType ;
5860 }
5961
62+ visitedEdmTypes . Add ( typeKey ) ;
63+
6064 const BindingFlags bindingFlags = BindingFlags . Public | BindingFlags . Instance ;
6165
6266 var properties = new List < ClassProperty > ( ) ;
6367 var structuralProperties = structuredType . Properties ( ) . ToDictionary ( p => p . Name , StringComparer . OrdinalIgnoreCase ) ;
6468 var clrTypeMatchesEdmType = true ;
69+ var hasUnfinishedTypes = false ;
70+ var dependentProperties = new Dictionary < string , Tuple < EdmTypeKey , bool > > ( ) ;
6571
6672 foreach ( var property in clrType . GetProperties ( bindingFlags ) )
6773 {
@@ -73,14 +79,61 @@ public Type NewStructuredType( IEdmStructuredType structuredType, Type clrType,
7379
7480 var structuredTypeRef = structuralProperty . Type ;
7581 var propertyType = property . PropertyType ;
82+ var propertyTypeKey = new EdmTypeKey ( structuredTypeRef , apiVersion ) ;
7683
7784 if ( structuredTypeRef . IsCollection ( ) )
7885 {
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+ }
80118 }
81119 else if ( structuredTypeRef . IsStructured ( ) )
82120 {
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+ }
84137 }
85138
86139 clrTypeMatchesEdmType &= propertyType . IsDeclaringType ( ) || property . PropertyType . Equals ( propertyType ) ;
@@ -94,10 +147,28 @@ public Type NewStructuredType( IEdmStructuredType structuredType, Type clrType,
94147
95148 var signature = new ClassSignature ( clrType . FullName , properties , apiVersion ) ;
96149
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+ }
99167
100- return generatedType ;
168+ return typeBuilder ;
169+ }
170+
171+ return generatedEdmTypes . GetOrAdd ( typeKey , CreateTypeInfoFromSignature ( signature ) ) ;
101172 }
102173
103174 /// <inheritdoc />
@@ -111,97 +182,107 @@ public Type NewActionParameters( IEdmAction action, ApiVersion apiVersion )
111182 var properties = action . Parameters . Where ( p => p . Name != "bindingParameter" ) . Select ( p => new ClassProperty ( assemblies , p ) ) ;
112183 var signature = new ClassSignature ( name , properties , apiVersion ) ;
113184
114- return generatedTypes . GetOrAdd ( signature , CreateFromSignature ) ;
185+ return CreateTypeInfoFromSignature ( signature ) ;
115186 }
116187
117- TypeInfo CreateFromSignature ( ClassSignature @class )
188+ TypeInfo CreateTypeInfoFromSignature ( ClassSignature @class )
118189 {
119190 Contract . Requires ( @class != null ) ;
120191 Contract . Ensures ( Contract . Result < TypeInfo > ( ) != null ) ;
121192
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+
123201 var moduleBuilder = modules . GetOrAdd ( @class . ApiVersion , CreateModuleForApiVersion ) ;
124202 var typeBuilder = moduleBuilder . DefineType ( @class . Name , TypeAttributes . Class ) ;
125203
126204 foreach ( var property in @class . Properties )
127205 {
128206 var type = property . GetType ( typeBuilder ) ;
129207 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 ) ;
148209
149210 foreach ( var attribute in property . Attributes )
150211 {
151212 propertyBuilder . SetCustomAttribute ( attribute ) ;
152213 }
153214 }
154215
155- return typeBuilder . CreateTypeInfo ( ) ;
216+ return typeBuilder ;
156217 }
157218
158- Type NewStructuredTypeOrSelf ( EdmTypeKey declaringTypeKey , IEdmCollectionTypeReference collectionType , Type clrType , ApiVersion apiVersion )
219+ private Type ResolveDependencies ( TypeBuilder typeBuilder , EdmTypeKey typeKey )
159220 {
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 ( ) ;
171222
172- var structuredType = elementType . ToStructuredType ( ) ;
173-
174- if ( declaringTypeKey == new EdmTypeKey ( structuredType , apiVersion ) )
223+ foreach ( var key in keys )
175224 {
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+ }
183244
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+ }
187259 }
188260
189- return clrType ;
261+ return typeBuilder ;
190262 }
191263
192- Type NewStructuredTypeOrSelf ( EdmTypeKey declaringTypeKey , IEdmStructuredType structuredType , Type clrType , ApiVersion apiVersion )
264+ static PropertyBuilder AddProperty ( TypeBuilder addTo , Type shouldBeAdded , string name )
193265 {
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 ;
205286 }
206287
207288 static ModuleBuilder CreateModuleForApiVersion ( ApiVersion apiVersion )
0 commit comments