What’s FodyWeaver

In almost all .NET applications you encounter lot of boilerplate code which although has pretty standard implementation but is quite important from implementation point of view. Some common examples are overridden ToString, Equals  method for you model classes. Or implementing IDisposable or INotifyPropertyChanged. In all these examples most of the code is pretty standard.

One of the ways of automating this code is to automatically inject this in your generated MSIL (which stands for Microsoft intermediate language).This process of injecting code post compilation directly into generated intermediate language is known as .NET assembly weaving or IL weaving (similar to byte code weaving in Java).

If you do this before compilation i.e. add source code lines before compilation it is known as Source code weaving.

What is Fody and how it works.

Fody is an extensible library for weaving .NET assembly written by Simon Cropp. It adds post build task in MS build pipeline to manipulate generated IL. Usually this requires lot of plumbing code which is what fody provides. Fody provides extensible add-in model where anybody can use core fody package (providing basic code for adding post build task and manipulating IL) and create their own specific add-ins. E.g. Equals. Fody generates Equals and GetHashCode method implementation and ToString. Fody generates ToString implementation for your classes.

Below figure shows how the whole process works.

You can also use the same technique for implementing AOP, logging, profiling methods etc.

Fody uses Mono.Cecil which is a library for manipulating intermediate language and in usage feels quite similar to .NET reflection APIs. As far as IL manipulation goes Mono.Cecil seems like only game in the town. Chances are high that if you have used plug-in for IL manipulation they would be using Mono.Cecil internally.

Lets try one of the fody add-ins…

For the purpose of this blog I would demonstrate one very useful fody add-in called NullGaurd. As name suggests this add-in automatically adds code for raising exceptions when null value is encountered in a property or a method parameter. Below are steps for adding plug-in and using it.

  • Create a sample project and add NullGuard.Fody nuget package
  • Now by default all the class properties, method parameters and return types will have null checks injected i.e. if any of these is null an exception will be thrown. But you still can allow nulls wherever required by using [AllowNull] attribute as shown below.
class NullGaurdTestClass
{
    [AllowNull]
    public string PropertyThatAllowsNull { get; set; }
    public string PropertyThatDoesNotAllowsNull { get; set; }
    public void TestMethod([AllowNull] string allowsNull, string doesNotAllowNull)
    {


    }
    public string TestMethodDoesNotAllowNullReturnValue()
    {
        return null;
    }
}
  • Running the code gives following result
  • You can control the behavior of addin by applying assembly attribute as shown below. Basically making ValidationFlags.None will stop adding null checks and ValidationFlags. All will add the behavior for everything.

Let’s decompile the generated assembly to see what code add-in has injected. Below is what you get. And that is the magic of IL weaving.

namespace NullGaurdFodySample

{

 internal class NullGaurdTestClass

 {

 public string PropertyThatAllowsNull

 {

 get;

 set;

 }

 public string PropertyThatDoesNotAllowsNull

 {

 [CompilerGenerated]

 get

 {

 string text = this.<PropertyThatDoesNotAllowsNull>k__BackingField;

 string expr_0A = text;

 Debug.Assert(expr_0A != null, "[NullGuard] Return value of property 'System.String NullGaurdFodySample.NullGaurdTestClass::PropertyThatDoesNotAllowsNull()' is null.");

 if (expr_0A == null)

 {

 throw new InvalidOperationException("[NullGuard] Return value of property 'System.String NullGaurdFodySample.NullGaurdTestClass::PropertyThatDoesNotAllowsNull()' is null.");

 }

 return expr_0A;

 }

 [CompilerGenerated]

 set

 {

 Debug.Assert(value != null, "[NullGuard] Cannot set the value of property 'System.String NullGaurdFodySample.NullGaurdTestClass::PropertyThatDoesNotAllowsNull()' to null.");

 if (value == null)

 {

 throw new ArgumentNullException("value", "[NullGuard] Cannot set the value of property 'System.String NullGaurdFodySample.NullGaurdTestClass::PropertyThatDoesNotAllowsNull()' to null.");

 }

 this.<PropertyThatDoesNotAllowsNull>k__BackingField = value;

 }

 }

 public void TestMethod(string allowsNull, string doesNotAllowNull)

 {

 Debug.Assert(doesNotAllowNull != null, "[NullGuard] doesNotAllowNull is null.");

 if (doesNotAllowNull == null)

 {

 throw new ArgumentNullException("doesNotAllowNull", "[NullGuard] doesNotAllowNull is null.");

 }

 }

 public string TestMethodDoesNotAllowNullReturnValue()

 {

 string text = null;

 string expr_06 = text;

 Debug.Assert(expr_06 != null, "[NullGuard] Return value of method 'System.String NullGaurdFodySample.NullGaurdTestClass::TestMethodDoesNotAllowNullReturnValue()' is null.");

 if (expr_06 == null)

 {

 throw new InvalidOperationException("[NullGuard] Return value of method 'System.String NullGaurdFodySample.NullGaurdTestClass::TestMethodDoesNotAllowNullReturnValue()' is null.");

 }

 return expr_06;

 }

 }

}

 

Leave a Reply

Your email address will not be published. Required fields are marked *