static Type GenerateTypeIfNeeded(IEdmStructuredType structuredType, BuilderContext context)
        {
            var typeKey = new EdmTypeKey(structuredType, context.ApiVersion);

            if (context.EdmTypes.TryGetValue(typeKey, out var generatedType))
            {
                return(generatedType);
            }

            var clrType         = structuredType.GetClrType(context.EdmModel) !;
            var visitedEdmTypes = context.VisitedEdmTypes;

            visitedEdmTypes.Add(typeKey);

            var properties           = new List <ClassProperty>();
            var structuralProperties = new Dictionary <string, IEdmProperty>(StringComparer.OrdinalIgnoreCase);
            var mappedClrProperties  = new Dictionary <PropertyInfo, IEdmProperty>();
            var dependentProperties  = new List <PropertyDependency>();

            MapEdmPropertiesToClrProperties(context.EdmModel, structuredType, structuralProperties, mappedClrProperties);

            var(clrTypeMatchesEdmType, hasUnfinishedTypes) =
                BuildSignatureProperties(
                    clrType,
                    structuralProperties,
                    mappedClrProperties,
                    properties,
                    dependentProperties,
                    context);

            return(ResolveType(
                       typeKey,
                       clrType,
                       clrTypeMatchesEdmType,
                       hasUnfinishedTypes,
                       properties,
                       dependentProperties,
                       context));
        }
        static Type GenerateTypeIfNeeded(IEdmStructuredType structuredType, BuilderContext context)
        {
            var apiVersion = context.ApiVersion;
            var edmTypes   = context.EdmTypes;
            var typeKey    = new EdmTypeKey(structuredType, apiVersion);

            if (edmTypes.TryGetValue(typeKey, out var generatedType))
            {
                return(generatedType);
            }

            var edmModel        = context.EdmModel;
            var clrType         = structuredType.GetClrType(edmModel);
            var visitedEdmTypes = context.VisitedEdmTypes;

            visitedEdmTypes.Add(typeKey);

            const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;

            var properties            = new List <ClassProperty>();
            var structuralProperties  = structuredType.Properties().ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
            var clrTypeMatchesEdmType = true;
            var hasUnfinishedTypes    = false;
            var dependentProperties   = new List <PropertyDependency>();

            foreach (var property in clrType.GetProperties(bindingFlags))
            {
                if (!structuralProperties.TryGetValue(property.Name, out var structuralProperty))
                {
                    clrTypeMatchesEdmType = false;
                    continue;
                }

                var structuredTypeRef = structuralProperty.Type;
                var propertyType      = property.PropertyType;
                var propertyTypeKey   = new EdmTypeKey(structuredTypeRef, apiVersion);

                if (structuredTypeRef.IsCollection())
                {
                    var collectionType = structuredTypeRef.AsCollection();
                    var elementType    = collectionType.ElementType();

                    if (elementType.IsStructured())
                    {
                        visitedEdmTypes.Add(propertyTypeKey);

                        var itemType   = elementType.Definition.GetClrType(edmModel);
                        var elementKey = new EdmTypeKey(elementType, apiVersion);

                        if (visitedEdmTypes.Contains(elementKey))
                        {
                            clrTypeMatchesEdmType = false;
                            hasUnfinishedTypes    = true;
                            dependentProperties.Add(new PropertyDependency(elementKey, true, property.Name));
                            continue;
                        }

                        var newItemType = GenerateTypeIfNeeded(elementType.ToStructuredType(), context);

                        if (newItemType is TypeBuilder)
                        {
                            hasUnfinishedTypes = true;
                        }

                        if (!itemType.Equals(newItemType))
                        {
                            propertyType          = IEnumerableOfT.MakeGenericType(newItemType);
                            clrTypeMatchesEdmType = false;
                        }
                    }
                }
                else if (structuredTypeRef.IsStructured())
                {
                    if (!visitedEdmTypes.Contains(propertyTypeKey))
                    {
                        propertyType = GenerateTypeIfNeeded(structuredTypeRef.ToStructuredType(), context);

                        if (propertyType is TypeBuilder)
                        {
                            hasUnfinishedTypes = true;
                        }
                    }
                    else
                    {
                        clrTypeMatchesEdmType = false;
                        hasUnfinishedTypes    = true;
                        dependentProperties.Add(new PropertyDependency(propertyTypeKey, false, property.Name));
                        continue;
                    }
                }

                clrTypeMatchesEdmType &= property.PropertyType.Equals(propertyType);
                properties.Add(new ClassProperty(property, propertyType));
            }

            var type = default(TypeInfo);

            if (clrTypeMatchesEdmType)
            {
                if (!edmTypes.TryGetValue(typeKey, out type))
                {
                    edmTypes.Add(typeKey, type = clrType.GetTypeInfo());
                }

                return(type);
            }

            var signature = new ClassSignature(clrType, properties, apiVersion);

            if (hasUnfinishedTypes)
            {
                if (edmTypes.TryGetValue(typeKey, out type))
                {
                    return(type);
                }

                var typeBuilder  = CreateTypeBuilderFromSignature(context.ModuleBuilder, signature);
                var dependencies = context.Dependencies;

                foreach (var propertyDependency in dependentProperties)
                {
                    propertyDependency.DependentType = typeBuilder;
                    dependencies.Add(propertyDependency);
                }

                edmTypes.Add(typeKey, typeBuilder);
                return(typeBuilder);
            }

            if (!edmTypes.TryGetValue(typeKey, out type))
            {
                edmTypes.Add(typeKey, type = CreateTypeInfoFromSignature(context.ModuleBuilder, signature));
            }

            return(type);
        }