private static void CreateOracleCollectionType(ModuleBuilder customTypeModuleBuilder, OracleTypeCollection collectionType, IDictionary <string, Type> customTypes)
        {
            var targetType         = typeof(object);
            var enclosingCharacter = String.Empty;

            if (collectionType.ElementDataType != null)
            {
                if (collectionType.ElementDataType.FullyQualifiedName.HasOwner)
                {
                    if (customTypes.TryGetValue(collectionType.ElementDataType.FullyQualifiedName.ToString().Replace("\"", null), out Type targetCustomType))
                    {
                        targetType = targetCustomType;
                    }
                }
                else
                {
                    targetType = OracleToNetTypeMapper.MapOracleTypeToNetType(collectionType.ElementDataType.FullyQualifiedName);

                    if (targetType == typeof(string))
                    {
                        enclosingCharacter = "'";
                    }
                }
            }

            var baseFactoryType = typeof(OracleTableValueFactoryBase <>);

            baseFactoryType = baseFactoryType.MakeGenericType(targetType);
            var fullyQualifiedCollectionTypeName = collectionType.FullyQualifiedName.ToString().Replace("\"", null);;
            var customTypeClassName = $"{DynamicAssemblyNameBase}.CollectionTypes.{MakeValidMemberName(fullyQualifiedCollectionTypeName)}";

            var customTypeBuilder = customTypeModuleBuilder.DefineType(customTypeClassName, TypeAttributes.Public | TypeAttributes.Class, baseFactoryType);

            AddOracleCustomTypeMappingAttribute(customTypeBuilder, fullyQualifiedCollectionTypeName);

            var baseConstructor = baseFactoryType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);

            AddConstructor(customTypeBuilder, baseConstructor);

            ImplementAbstractStringValueProperty(customTypeBuilder, "FullyQualifiedName", fullyQualifiedCollectionTypeName);
            ImplementAbstractStringValueProperty(customTypeBuilder, "EnclosingCharacter", enclosingCharacter);
            ImplementAbstractStringValueProperty(customTypeBuilder, "ElementTypeName", collectionType.ElementDataType.FullyQualifiedName.ToString());

            customTypes.Add(fullyQualifiedCollectionTypeName, customTypeBuilder.CreateType());
        }
        private static void CreateOracleObjectType(ModuleBuilder customTypeModuleBuilder, OracleTypeObject objectType, IDictionary <string, Type> customTypes)
        {
            var fullyQualifiedObjectTypeName = objectType.FullyQualifiedName.ToString().Replace("\"", null);
            var customTypeClassName          = $"{DynamicAssemblyNameBase}.ObjectTypes.{MakeValidMemberName(fullyQualifiedObjectTypeName)}";
            var customTypeBuilder            = customTypeModuleBuilder.DefineType(customTypeClassName, TypeAttributes.Public | TypeAttributes.Class);

            customTypeBuilder.SetParent(typeof(OracleCustomTypeBase <>).MakeGenericType(customTypeBuilder));
            AddOracleCustomTypeMappingAttribute(customTypeBuilder, fullyQualifiedObjectTypeName);

            AddConstructor(customTypeBuilder, typeof(object).GetConstructor(Type.EmptyTypes));

            ImplementAbstractStringValueProperty(customTypeBuilder, "DataTypeName", fullyQualifiedObjectTypeName, MethodAttributes.Public);

            var fields = new Dictionary <string, FieldInfo>();

            foreach (var objectAttribute in objectType.Attributes)
            {
                var targetType = OracleToNetTypeMapper.MapOracleTypeToNetType(objectAttribute.DataType.FullyQualifiedName);

                var fieldName    = MakeValidMemberName(objectAttribute.Name);
                var fieldBuilder = customTypeBuilder.DefineField(fieldName, targetType, FieldAttributes.Public);

                var attributeType = typeof(OracleObjectMappingAttribute);
                var attribute     = new CustomAttributeBuilder(attributeType.GetConstructor(new[] { typeof(string) }), new[] { objectAttribute.Name.Replace("\"", null) });
                fieldBuilder.SetCustomAttribute(attribute);

                fields.Add(fieldName, fieldBuilder);
            }

            var ilGenerator = BuildOracleCustomTypeInterfaceMethod(customTypeBuilder, "FromCustomObject");

            foreach (var field in fields)
            {
                ilGenerator.Emit(OpCodes.Ldarg_1);
                ilGenerator.Emit(OpCodes.Ldarg_2);
                ilGenerator.Emit(OpCodes.Ldstr, field.Key);
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldfld, field.Value);

                if (field.Value.FieldType.IsValueType)
                {
                    ilGenerator.Emit(OpCodes.Box, field.Value.FieldType);
                }

                ilGenerator.Emit(OpCodes.Call, typeof(OracleUdt).GetMethod("SetValue", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(OracleConnection), typeof(IntPtr), typeof(string), typeof(object) }, null));
            }

            ilGenerator.Emit(OpCodes.Ret);

            ilGenerator = BuildOracleCustomTypeInterfaceMethod(customTypeBuilder, "ToCustomObject");;

            foreach (var field in fields)
            {
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldarg_1);
                ilGenerator.Emit(OpCodes.Ldarg_2);
                ilGenerator.Emit(OpCodes.Ldstr, field.Key);

                ilGenerator.Emit(OpCodes.Call, typeof(OracleUdt).GetMethod("GetValue", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(OracleConnection), typeof(IntPtr), typeof(string) }, null));

                if (field.Value.FieldType.IsValueType)
                {
                    ilGenerator.Emit(OpCodes.Unbox_Any, field.Value.FieldType);
                }

                ilGenerator.Emit(OpCodes.Stfld, field.Value);
            }

            ilGenerator.Emit(OpCodes.Ret);

            var customType = customTypeBuilder.CreateType();

            customTypes.Add(fullyQualifiedObjectTypeName, customType);

            /*var schemaName = objectType.FullyQualifiedName.Owner.Trim('"');
             * var typeName = objectType.FullyQualifiedName.Name.Trim('"');
             * var mappingTableKey = String.Format("schemaName='{0}' typeName='{1}'", schemaName, typeName);
             * var mappingTableValue =
             *      new NameValueCollection
             *      {
             *              { "schemaName", schemaName },
             *              { "typeName", typeName },
             *              { "factoryName", customType.FullName }
             *      };
             *
             * mappingTable.Add(mappingTableKey, mappingTableValue);*/
        }