I’ve been wanting to look into Fody for a while now, I find it a little annoying when I have to pollute code that actually is a functional part of an application with a mass of boilerplate code, which could be automatically generated. Obviously we can look at T4 templates, partial classes and other ways to achieve such things, but a nice AOP style way is to “code weave” the boiler plate at compile time.
Note: To really get the benefit of Fody you need to understand Mono.Cecil which allows us a simpler way to write our IL. This is outside the scope of this post.
I’m not intending to go too in depth with Fody in this post, but I am going to describe creating a really pointless ModuleWeaver (the class/code which weaves in our new IL code) to add a new type to your assembly as a great starting point for more interesting/impressive code.
I’m going to show how to develop some code into a separate solution and use from a Test application (which sounds easy, and is once you know the expectations of Fody, but took me a while to find those expectations due to some tutorials being out of date) and I’ll also show how to develop a ModuleWeaver within an application’s solution.
Let’s create our first weaver
- Create a class library project named TestWeaver.Fody.
Note: The .Fody part is important as Fody will search for DLL’s using *.Fody.DLL - Add NuGet package FodyCecil
- Create/rename you Class1 as/to ModuleWeaver.cs
- Include the following code in the ModuleWeaver class
public class ModuleWeaver { public ModuleDefinition ModuleDefinition { get; set; } public void Execute() { ModuleDefinition.Types.Add( new TypeDefinition("TestNamespace", "TestType", TypeAttributes.Public, ModuleDefinition.ImportReference(typeof(object)))); } }
Note: The basic source for Execute is taken from ModuleWeaver.
Creating a test application
- Create a console application (we’re not actually going to write any console code for this, so we could have used a different project type)
- Add NuGet packages Fody (and this should add FodyCecil)
- Change the FodyWeavers.xml to look like this
<?xml version="1.0" encoding="utf-8"?> <Weavers> <TestWeaver /> </Weavers>
Deploying our weaver assembly
Okay, now if you build this ofcourse it will fail, we need to deploy the previously created TestWeaver.Fody somewhere. As we’re not using NuGet to deploy it and not including it in the solution, we have to place the DLL in a specific location so Fody knows where to find it.
Note: Remember, these DLL’s are solely for code weaving and not required in the deployed application, hence we do not need these files to go into the bin folder and we do not need to reference any *.Fody.DLL assemblies as they’re just used by Fody to change our IL (unless you’ve put attributes or the likes in the DLL – best practise would be to have these in a separate DLL which is referenced).
Add a folder named Tools to the to your solutions folder. i.e. SolutionDir/Tools.
Drop the files from your TestWeaver.Fody into this folder and now try to rebuild our test application. Now Fody should use you waver DLL to change the IL. If you open the application assembly in ILSpy (or the likes), you should see Fody (via our TestWeaver) created the TestNamespace and TestType we defined in our ModuleWeaver.
Including our Weaver in our solution
If you’ve covered the steps above, now delete the SolutionDir/Tools folder as we’re going to instead create our Weaver in a project within the test application solution.
In this case
- Create a class library named Weavers in our test application
- Create a class/rename the Class1 to TestWeaver.cs
- Add this code to TestWeaver
public class TestWeaver { public ModuleDefinition ModuleDefinition { get; set; } public void Execute() { ModuleDefinition.Types.Add( new TypeDefinition("NewTestNamespace", "NewTestType", TypeAttributes.Public, ModuleDefinition.ImportReference(typeof(object)))); } }
Note: I’ve renamed the namespace and type within the generated IL just to ensure we notice the difference in ILSpy
Notice (again) that we do not reference this project, but we will need the project to be built before our test application. So select the solution and right mouse click, select Project Build Order and you’re probably see Weavers listed after the test application, hence select the Dependencies tab, ensure your test application project is select in the Projects drop down and tick Depends On Weavers.
Now rebuild, inspect the resultant assembly using ILSpy. The namespace and type added should have their names changes as we’re now generating our IL code via the solution.
Debugging your Weaver
Obviously, as the weaving takes place as part of the build, we’re going to need to attach our debugger to a build or run the build via a debugger. Initially I simply placed a Debugger.Break() in my Execute method on my weaver module and clicked the build button. This worked the first time but not subsequent times and required the weaver to be in the same solution as the test application, so we’d be best to run msbuild directly from our project and debug via that application.
Here’s a comprehensive post on debugging msbuild – Debugging a Fody Weaver (Plugin) and/or debugging MSBuild.
We can also add logging to our application via some of the other possible insertion points in our Module Weaver, foe example, take a look at ModuleWeaver and you’ll see the “option members” include LogWarning, LogError, LogInfo (and others) which allows us to output log information during the build.
References
Simple AOP with Fody
ModuleWeaver
Fody on github
Creating a Fody Add-in