Search code examples
c#reflectionruntimebuild-process

(How) Can I query classes "post-build", using reflection in C#.Net?


Scenario:

  • I have a list of .cs files in a visual studio project (e.g., MyPOCOs), housing a bunch of POCOs (Plain Old C# Objects)
  • I have to run a custom serialization tool (homegrown and I have access to source code) that converts these POCOs into XML files - the tool does this via reflection.

Currently, MyPOCOs project is marked as a Console App and has a main method like the one below:

class Program
    { 
        static void Main(string[] args)
        {
            //Initialize custom XML Serializer
            GenerateBulkVmXML batchSerializer = new GenerateBulkVmXML();

            //Add individual POCOs to Serializer
            batchSerializer.AddPocoModel(typeof(MyPOCO1), "My.Custom.NameSpace");
            batchSerializer.AddPocoModel(typeof(MyPOCO2), "My.Custom.NameSpace1");
            batchSerializer.AddPocoModel(typeof(MyPOCO3), "My.Custom.NameSpace2");

            //Generate custom XML
            batchSerializer .WriteXML(); 
        }
}

Everytime we "run" the above console app, it triggers the Main method, that queries the POCO classes via reflection and spews out the XML.

Problem: For every new POCO added to the project, we have add a line to the above Main method calling the batchSerializer.AddPocoModel(...) method by passing they type of the newly created POCO. Overtime, the number of POCOs will increase and this well get rather unwieldy.

Proposed Solution: My team suggests we integrate this process with the "build" of the MyPOCO project, but I'm not sure everyone can grasp the viability of this approach. So, when we 'build' the project in visual studio, the above Main method gets executed, in essence. Here are the two problems I see with this approach:

  1. How can I discover the classes to serialize? Currently I add them to the main method via the AddPocoModel method. But if I don't do that, how can I discover them and query them via reflection?
  2. How exactly do I trigger the above main method post-build (i.e., it could be any method in a custom class. I'm just using the Main for the sake of argument. Basically, how can I trigger the method that does what the main method does above)?

How do I achieve this? Or, is there a better solution to the above problem?


Solution

  • I would break out your serializer into a separate project, a console application would be just fine. This application should take an argument pointing to the DLL that was just compiled, you can then create a post-build command that runs this tool and passes in the DLL name. On the plus side, this creates a reusable tool for other projects. There are a number of macros, but yours would end up looking something like:

    "c:\path\to\serialization\tool.exe" $(TargetPath)

    As for discovering the types, since there is probably nothing differentiating your POCO model classes from other classes (if there are other classes in the DLL), so you will need to mark them somehow. I'd go with a custom attribute:

    [AttributeUsage(AttributeTargets.Class, Inherited = false)]
    public class SerializePOCOAttribute : Attribute { }
    

    Then you can use a discovery method for enumerating all the classes in the DLL and serializing them, something like:

    public class POCOSerializer
    {
        public static void Serialize(string dllFileName, GenerateBulkVmXML batchSerializer)
        {
            var assem = Assembly.LoadFrom(dllFileName);
    
            var pocoObjects = assem.GetTypes().Where(t => t.GetCustomAttribute<SerializePOCOAttribute>() != null);
    
            foreach (var poco in pocoObjects)
            {
                batchSerializer.AddPocoModel(poco, poco.Namespace);
            }
    
            batchSerializer.WriteXML();
        }
    }