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

2

7

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);

    }
Scup answered 23/2, 2012 at 13:28 Comment(3)
Take a look at hello, world... Reflection.Emit style!Indonesian
Does emit specifically create a .DLL or .EXE? I just need a class in memory, nothing saved to disk.Scup
I was saying that I do NOT need to save to disk.Scup
H
3

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); 
} 
Hilde answered 23/2, 2012 at 14:30 Comment(3)
Thanks, I will try shortly. I need to do something like this to use it: FileRow fileRow = ParseFileHelperDataRow(rowID, lineContents, sqlConn); So once I do what you show above, then I would use reflection to create my object?Scup
After you do what I showed you will have now object in your assembly. If you look at my example bellow it shows how you can call properties on that object, but you can also assign properties/fields, call methods on it. For instance if you have method you could get that method assembly.GetType("FileRow").GetMethod("MyMethod); and then call invoke on it. assembly variable that you get is fully compiled your code and you can do with it anything like regular objects/compiled code.Hilde
So I need to populate my object with values, before I try to extract those values; I'll try something like this: Object myObject = assembly.GetType("FileRow"); myObject = ParseFileHelperDataRow(rowID, lineContents, sqlConn);Scup
A
2

You can also use CodeDOM to generate your class dynamically. For more information visit

http://msdn.microsoft.com/en-us/library/ms404245.aspx

Analogical answered 23/2, 2012 at 14:1 Comment(5)
That looks easy; I have not even heard of it... It can do the attributes too I suppose?Scup
I see it is also using Streamwriter to write to disk. Can I just create the class and use it in my code without writing to disk?Scup
Yes we can using CustomAttributes property.Analogical
Yes, you can just create an assembly. Instead of provider.GenerateCodeFromCompileUnit use provider.CompileAssemblyFromDom methodAnalogical
I think this would get me to the same end as Vlad's answer above, but there seemed to be less of a learning curve for me there, building the C# code in text, and compiling it, vs having to learn the CodeDOM structure.Scup

© 2022 - 2024 — McMap. All rights reserved.