Code Generation Chronicles
As part of my new year resolutions, I’ve decided to put more effort in learning code generation techniques.
This is the first blog post in a series exploring code generation. Although I won’t always dive into the internals, I do promise that I’ll show examples of what we can achieve with each technique and when it is better to use each one.
Fody
Fody is an open source library for IL weaving. It can generate and manipulate IL code during build time.
Getting Fody is easy – it is available as a Nuget Package and doesn’t require any installation/modification to the build system.
One of the benefits of using Fody is that there is no footprint – since the IL weaving is done during build time, there aren’t any Fody-related assemblies needed at runtime, which could be a good thing if you worried about your project dependencies or the number of assemblies you ship with your product.
And on top of that Fody is highly extensible and there is an active open source community around it.
One example for how Fody can save developer’s time is such extension: “PropertyChanged” which works by placing the ImplementPropertyChanged attribute it provides on a class, it will generate all of the code necessary to fully implement the INotifyPropertyChanged interface in that class.
More extensions can be found here.
Using Fody to add WCF’s KnownType attributes
At work we’ve used a client which was implemented using .Net Remoting as the communication model for it services. At some point, we wanted to exchange .Net Remoting with the more modern WCF. The problem was that a lot of the code base was unmaintainable legacy code, and we’ve wanted to perform as little changes as possible, and reuse as much of the existing architecture as we could.
One of the challenges resided in a WCF quirk, were by default it doesn’t allow passing a derived type of the DataContract type that is defined in the OperationContract.
The way to make derived classes work is done in WCF by adding a KnownType attribute on the base type for each derived type we use.
As an example, if we have a class A and two derived classes B and C, we would have to add two KnownType attributes over class A; one for Class B and one for Class C.
[KnownType(typeof(B))] [KnownType(typeof(C))] class A { } class B : A { } class C : A { }
Since this scenario was common in the current architecture, we were looking for a way to solve this issue without manually adding KnownType attributes, since it would be easy to miss some or to forget adding the attribute over new derived classes in the future.
In the end we’ve managed to save time and replace the communication layer easily by using Fody for automatically adding KnownType attributes for all derived types on their base classes during build time.
Implementing a Fody extension
If you want to write Fody extension start by cloning the BasicFodyAddin repository. This repository is maintained by the open source community and it simplifies both the implementation and the deployment of the extension as a Nuget Package.
BasicFodyAddin source code contains a Solution with four C# projects:
- BasicFodyAddin which will contain the extension
- Nuget which is for deploying a Nuget package
- Tests for writing unit tests
- AssemblyToProcess that is used as a target assembly for testing your mew Fody extension.
We will focus on BasicFodyAddin.
In the BasicFodyAddin project there is a file called ModuleWeaver.cs Go ahead and replace it content with the following code:
using System; using System.Linq; using Mono.Cecil; using Mono.Cecil.Rocks; namespace KnownTypes.Fody { public class ModuleWeaver { public ModuleDefinition ModuleDefinition { get; set; } public void Execute() { } void AddKnownTypeAttributes(ModuleDefinition module, TypeDefinition baseType) { } void RemoveKnowsDeriveTypesAttribute(TypeDefinition baseType) { } } }
This is the class we will use to implement the extension. The ModuleDefinition property will be populated during build time by Fody and it will contain the target Module for the IL Weaving.
As you have probably noticed, the property is of type ModuleDefinition.
ModuleDefinition is a class representing a MSIL Module in Mono.Cecil which is a library Fody relies on to generate and manipulate IL code. In addition to ModuleDefinition we will use more types it defines such as TypeDefinition.
When using Fody your ModuleWeaver class must be implemented according the following:
- Be public, instance and not abstract.
- Have an empty constructor.
- Have a ModuleDefinition property that will be populated during build.
- Have an Execute method.
The KnownTypeAttributeWeaver class has three main methods:
- Execute – Entry point. It calls other methods for performing the IL weaving.
- AddKnownTypeAttributes – Decorates base types with KnownType attributes.
- RemoveKnowsDeriveTypesAttribute – Removes KnowsDeriveTypes attributes from base types
Note: A few helper methods will be added down the road.
Now create another a new class called KnowsDeriveTypesAttribute.
[AttributeUsage(AttributeTargets.Class, Inherited = false)] public class KnowsDeriveTypesAttribute : Attribute { }
This attribute is for explicitly specifying the base types we want to decorate with KnownType attributes. We could have added KnownType attributes over every base class in the assembly, but we wanted the extra control.
Implementing Execute
public void Execute() { foreach (var type in ModuleDefinition.GetTypes().Where(HasKnownDeriveTypesAttribute)) { AddKnownTypeAttributes(module, type); RemoveKnowsDeriveTypesAttribute(type); } } //A helper method //For filtering the types without the KnowsDeriveTypes Attribute. bool HasKnownDeriveTypesAttribute(TypeDefinition type) => type.CustomAttributes.Any(attr => attr.AttributeType.FullName == typeof(KnowsDerivativeTypesAttribute).FullName);
In Execute we locate all the types decorated with KnowsDeriveTypesAttribute, and we decorate them with KnownType attributes according to their sub types.
After that is done, we remove the KnowsDeriveTypesAttribute as it isn’t necessary anymore.
Implementing AddKnownTypeAttributes
void AddKnownTypeAttributes(ModuleDefinition module, TypeDefinition baseType) { //Locate derived types var derivedTypes = GetDerivedTypes(module, baseType); //Gets a TypeDefinition representing the KnownTypeAttribute type. var knownTypeAttributeTypeDefinition = GetTypeDefinition(module, "System.Runtime.Serialization", "KnownTypeAttribute"); //Gets the constructor for the KnownTypeAttribute type. var knownTypeConstrcutor = GetConstructorForKnownTypeAttribute(module, knownTypeAttributeTypeDefinition); //Adds KnownType attribute for each derive type foreach (var derivedType in derivedTypes) { var attribute = new CustomAttribute(constructorToUse); attribute.ConstructorArguments.Add(new CustomAttributeArgument(constructorToUse.Parameters.First().ParameterType, derivedType)); baseType.CustomAttributes.Add(attribute); } }
Let’s break it down.
//Locate derived types var derivedTypes = GetDerivedTypes(module, baseType); //Gets a TypeDefinition representing the KnownTypeAttribute type. var knownTypeAttributeTypeDefinition = GetTypeDefinition(module, "System.Runtime.Serialization", "KnownTypeAttribute"); //Gets the constructor for the KnownTypeAttribute type. var knownTypeConstrcutor = GetConstructorForKnownTypeAttribute(module, knownTypeAttributeTypeDefinition);
In the code above we are performing three steps:
- Finding and retrieving all derived types of the given base type.
- Creating a TypeDefinition instance for the KnownType attribute.
- Finding and retrieving a constructor for the KnownType attribute.
The first step is done using a helper method called GetDerivedTypes.
Given a module and a type it returns an array of TypeDefinitions, containing all of the types in the given module that derive from the given type
The second step is done using a helper method called GetTypeDefinition.
Given an assembly name and a type name, it returns a TypeDefinition for that type.
The third step is done using a helper method called GetConstructorForKnownTypeAttribute.
Given a ModuleDefinition and a TypeDefinition it returns a the first constructor for that type.
//A helper method. Given a module and a baes type: //It returns all derived types of that base type. TypeDefinition[] GetDerivedTypes(ModuleDefinition module, TypeDefinition baseType) => module.GetTypes() .Where(type => type.BaseType?.FullName == baseType.FullName) .ToArray(); //A helper method. Given an assembly and a type name: //It returns a TypeDefinision for that type. TypeDefinition GetTypeDefinition(ModuleDefinition module, string assemblyName, string typeName) => module.AssemblyResolver .Resolve(assemblyName) .MainModule.Types.Single(type => type.Name == typeName); //A helper method. Given a module and type definition for the KnownType Attribute: //It returns the constructor for the attribute accepting a System.Type object. MethodReference GetConstructorForKnownTypeAttribute(ModuleDefinition module, TypeDefinition knownTypeAttributeTypeDefinition) { var constructorMethodToImport = knownTypeAttributeTypeDefinition .GetConstructors() .Single(ctor => 1 == ctor.Parameters.Count && "System.Type" == ctor.Parameters[0].ParameterType.FullName); return module.Import(constructorMethodToImport); }
Let’s continue and break down the last part of AddKnownTypeAttributes.
//Adds KnownType attribute for each derive type foreach (var derivedType in derivedTypes) { var attribute = new CustomAttribute(constructorToUse); attribute.ConstructorArguments.Add(new CustomAttributeArgument(constructorToUse.Parameters.First().ParameterType, derivedType)); baseType.CustomAttributes.Add(attribute); }
In this part of the code we add a KnownType attribute to the base type for each derived type we previously found.
We are using the KnownType attribute’s constructor that expects the System.Type of the sub class.
Implementing RemoveKnowsDeriveTypesAttribute
This part is straight forward. We locate and then remove the KnowsDeriveTypes attribute from the base type.
void RemoveKnowsDeriveTypesAttribute(TypeDefinition baseType) { var foundAttribute = baseType.CustomAttributes .Single(attribute => attribute.AttributeType.FullName == typeof(KnowsDeriveTypesAttribute).FullName); baseType.CustomAttributes.Remove(foundAttribute); }
Unit Tests & Debugging
Unit Tests are always important, especially when writing Fody extensions. We had a good code coverage in the final product, but for this blog post I’ll use Unit Tests solely for debugging purposes, as I will be able to debug the extension by running the Unit Tests in debug mode.
For this post I’ve created two Unit Tests. One for testing the addition of the KnownType attributes and one for testing the removal of the KnowsDeriveTypes attribute. You can view the Unit Tests file here. I’ve also went ahead and added three classes to the AssemblyToProcess project for testing the IL Weaving process.
Even though Fody has a basic logging capability it isn’t always easy to find and view logs in the output window.
Fortunately, at debug time we can use OzCode’s Trace Points feature. I’ve added a trace point at the line which adds a KnownType attribute over base types.
//Trace point was added here in the AddKnownTypeAttributes method. baseType.CustomAttributes.Add(attribute);
After running The Unit Tests in debug mode, add a breakpoint at the line above and then create a new Trace Point as follow:
You can customize your reports by writing expressions inside curly brackets:
After you run the tests, you can view the results by opening the following menu:
The results:
As a decisive test we can open the output assembly in a Decompiler and view the final results.
Fortunately, the Unit Tests have created a new version of the AssemblyToProcess.DLL containing the changes done by the IL Weaving.
The DLL is called AssemblyToProcess2.DLL and you can find it at the AssemblyToProcess bin directory.
For example, opening the DLL using JetBrain’s DotPeek decompiler yields the following result:
Fody and Mono.Cecil didn’t only add KnownType attributes for the derive types, but also removed the KnownDeriveTypes attribute and added a reference to System.Runtime.Serialization in which the KnownType attribute is defined in.
Deployment
To use and distribute your Fody extension you will need to create a NuGet package. I will write a dedicated blog post on how this is done and on how we can publish Nuget Packages, including the one for this extension.
Summary
Fody is a powerful tool. It runs during the build’s pipeline and it manipulate the code of assemblies using Mono.Cecil’s IL Weaving capabilities.
As the examples in this post and in the official list of Fody Addins, we can clearly see that Fody can be a pretty handy tool.
But it is really important to keep the following lesson in mind:
Use it wisely and try not to shoot your foot 😉
You can view/clone/fork the code for this extension from this Github repository.
Note: this post is published also at OzCode’s blog.