Search code examples
c#classreflectionreflection.emitfilehelpers

Use Reflection to Build a Class (to build a dynamic FileHelper class)


Can I build a class as shown below dynamically using reflection? There are no methods, just public variables, some have custom attributes.

Is the .Emit method required (from what I've seen, "Emit" looks a little challenging).

I'm using software from www.FileHelpers.net, and it requires a class. All my file definitions are in a database table, and I'd like to make everything more dynamic (i.e. no code changes when a new column appears in the file).

[FileHelpers.DelimitedRecord(",")]
public class FileRow
{
    [FileHelpers.FieldQuoted('"', QuoteMode.OptionalForBoth)] 
    public string Borrower_First_Name;
    [FileHelpers.FieldQuoted('"', QuoteMode.OptionalForBoth)] 
    public string Borrower_Last_Name;
    public string Borrower_Email;
}

Update 1: Based on Vlad's answer below I needed to reference DLL, here's how I did it:

    // need to reference the FileHelpers.dll from our own .exe directory 
    string diskFilenameFileHelpersDLL = 
        System.IO.Path.GetDirectoryName(
           System.Reflection.Assembly.GetExecutingAssembly().Location) + 
           @"\FileHelpers.dll";

Update 2: Also, after doing what Vlad suggested, this is how I call FileHelper and loop through the results. I'll probably transfer the data to a list.

    Assembly assembly = compiledResult.CompiledAssembly;

    // Simple Data Test 
    lineContents = "John,Doe,[email protected]";
    FileHelperEngine engine = new FileHelperEngine(assembly.GetType("FileRow"));
    // FileRow[] FileRowArray = (FileRow[])engine.ReadString(lineContents);
    Object[] FileRowArray = engine.ReadString(lineContents);
    Object myObject = FileRowArray[0];  // only 1 row of data in this example 

    // Get the type handle of a specified class.
    Type myType = assembly.GetType("FileRow");
    // Get the fields of the specified class.
    FieldInfo[] myField = myType.GetFields();

    Console.WriteLine("\nDisplaying fields values:\n");
    for (int i = 0; i < myField.Length; i++)
    {
        Object objTest = myField.GetValue(i);

        string tempName = myField[i].Name;
        Object objTempValue = myField[i].GetValue(myObject);
        string tempValue = System.Convert.ToString(objTempValue);

        Console.WriteLine("The value of {0} is: {1}",
                            tempName, tempValue);

    }

Solution

  • If you have your code stored in the database as string what you can do something like this to create an assembly:

    The reason I commented out attributes because I don't have namespace for them. I am assuming you have namespace and you will need to add it to your code to compile.

    Code works in LINQPad, so you can just copy and paste.

    using System;
    using System.Reflection;
    using System.CodeDom.Compiler;
    using Microsoft.CSharp;
    
    void Main()
    {
        StringBuilder dc = new StringBuilder(512);
        dc.Append("public class FileRow");
        dc.Append("{");
        //dc.Append("[FileHelpers.FieldQuoted('\"', QuoteMode.OptionalForBoth)]");
        dc.Append("public string Borrower_First_Name;");
        //dc.Append("[FileHelpers.FieldQuoted('\"', QuoteMode.OptionalForBoth)]");
        dc.Append("public string Borrower_Last_Name;");
        dc.Append("public string Borrower_Email;");
        dc.Append("}");
    
        CompilerResults compiledResult = CompileScript(dc.ToString());
    
        if (compiledResult.Errors.HasErrors)
        {
            Console.WriteLine (compiledResult.Errors[0].ErrorText);
            throw new InvalidOperationException("Invalid Expression syntax");
        }
    
        Assembly assembly = compiledResult.CompiledAssembly;
    
        // This is just for testing purposes.
        FieldInfo field = assembly.GetType("FileRow").GetField("Borrower_First_Name");          
        Console.WriteLine (field.Name);         
        Console.WriteLine (field.FieldType);
    }
    
    public static CompilerResults CompileScript(string source) 
    { 
        CompilerParameters parms = new CompilerParameters(); 
    
        parms.GenerateExecutable = false; 
        parms.GenerateInMemory = true; 
        parms.IncludeDebugInformation = false; 
    
        CodeDomProvider compiler = CSharpCodeProvider.CreateProvider("CSharp"); 
    
        return compiler.CompileAssemblyFromSource(parms, source); 
    }