Matt Matt - 3 months ago 16
C# Question

CodeDom - Create object that has a property of an array of objects

I'm attempting to create a complex object using CodeDom. Right now I have no issues with creating simple objects such as:

public class MyClass
{
public FirstName {get; set;}
public LastName {get; set;}
}

public class Subclass
{
public string SubProperty {get; set;}
}


But I'm having some trouble creating the following:

public class MyClass
{
public string FirstName {get; set;}
public string LastName {get; set;}
public Subclass[] SubClasses {get; set;}
}


Here is my current implementation.

public class cls_Code_Generator
{
private CodeNamespace CodeNamespace { get; set; }
private JSchema JsonSchema { get; set; }
public cls_Code_Generator()
{
CodeNamespace = new CodeNamespace("GeneratedCode");
CodeNamespace.Imports.Add(new CodeNamespaceImport("System"));
}
public Assembly CreateType(JSchema schema)
{
JsonSchema = schema;
CodeTypeDeclaration CodeType = new CodeTypeDeclaration();
CodeType.Name = "RuntimeType";
CodeType.IsClass = true;
CodeType.Attributes = MemberAttributes.Public;

CodeNamespace.Types.Add(CodeType);

CodeType = GetFields(CodeType);

CodeCompileUnit CodeUnit = new CodeCompileUnit();
CodeUnit.ReferencedAssemblies.AddRange(GetReferenceList());
CodeUnit.Namespaces.Add(CodeNamespace);
return CreateTypeInMemory(CodeUnit);
}

private Assembly CreateTypeInMemory(CodeCompileUnit codeUnit)
{
Assembly CompiledAssembly = null;

CompilerParameters CompilerParams = new CompilerParameters();
CompilerParams.ReferencedAssemblies.AddRange(new[] { "System.dll" });
CompilerParams.GenerateInMemory = true;
CompilerParams.GenerateExecutable = false;

CSharpCodeProvider Compiler = new CSharpCodeProvider();
CompilerResults Results = Compiler.CompileAssemblyFromDom(CompilerParams, codeUnit);

if (Results.Errors != null && Results.Errors.Count == 0)
{
CompiledAssembly = Results.CompiledAssembly;
}
return CompiledAssembly;
}

private CodeTypeDeclaration GetFields(CodeTypeDeclaration CodeType)
{
CodeType.Members.AddRange(GetGeneratedFieldCollection());
return CodeType;
}

private CodeTypeMember[] GetGeneratedFieldCollection()
{
var codeMemberFieldList = new List<CodeMemberField>();
foreach (var property in JsonSchema.Properties)
{
codeMemberFieldList.Add(new CodeMemberField
{
Type = new CodeTypeReference(JSchemaTypeHelper.GetJSchemaSystemType(property.Value.Type)),
Name = property.Key,
Attributes = MemberAttributes.Public
});

}
return codeMemberFieldList.ToArray();
}

private string[] GetReferenceList()
{
List<string> references = new List<string>();
references.AddRange(new string[] { "System", "System.Collections", "System.Collections.Generic" });
return references.ToArray();
}

}

Answer

So I ended up figuring it out, I had to first generate the subclass and then create a List using the type of the generated subclass.

 private CodeTypeMember[] GetGeneratedFieldCollection(JSchema schema)
    {
         var codeMemberFieldList = new List<CodeMemberField>();
        foreach (var property in schema.Properties)
        {
            if(property.Value.Type.ToString() != "Array, Null")
            {
                codeMemberFieldList.Add(new CodeMemberField
                {
                    Type = new CodeTypeReference(JSchemaTypeHelper.GetJSchemaSystemType(property.Value.Type)),
                    Name = property.Key,
                    Attributes = MemberAttributes.Public
                });
            }
            else
            {
                var subTypeAssembly = CreateSubType("SubRunTimeType", property.Value.Items.FirstOrDefault());
                var type = subTypeAssembly.DefinedTypes.Skip(1).FirstOrDefault();
                Type listType = typeof(List<>).MakeGenericType(type);
                var codeMemberField = new CodeMemberField
                {
                    Type = new CodeTypeReference(listType),
                    Name = property.Key,
                    Attributes = MemberAttributes.Public
                };
                codeMemberFieldList.Add(codeMemberField);
            }
        }
        return codeMemberFieldList.ToArray();
    }