Saturday, May 25, 2013

Circular references between class libraries

Note: the demo project for this blog entry is on GitHub: https://github.com/bfriesen/CircularReferenceProblem.

Recently at work, we ran into a situation where we were building an internal library. Most of the library was written in C#, but some of it - complex statistical calculations - was better suited for F#. This posed a problem because the F# code needed to use some of the objects that were defined in the C# code, and the C# code needed to call the F# calculation code. Because C# and F# cannot exist in the same project, we had two projects on our hands, each of which needed to reference the other. This would cause a circular reference, and Visual Studio won't allow this to happen.

This post describes a similar situaion, and a pretty elegant solution to the problem.

The scenario

In our fake scenario, we're building a library with C# that provides a Calculator class. This class has one method, Add, that takes two Number objects. It should convert those Number objects into double values and return the result of adding those values. Now, you're going to have to suspend disbelief for a minute, but, for some reason, the developers had great difficulty in converting a Number object into a double. They determined that the best way of doing the converting was to use F#. They have created a NumberConverter class to handle this parsing.

The Number class (in the C# project):

public class Number
{
    public string Value { get; set; }
}

The NumberConverter class (in the F# project):

public class NumberConverter
{
    // Pretend really hard that this is written in F#. :)
    public double Parse(Number number)
    {
        return double.Parse(number.Value);
    }
}

The Calculator class (in the C# project):

public class Calculator
{
    private readonly NumberConverter _numberConverter;

    public Calculator()
    {
        _numberConverter = new NumberConverter();
    }

    public double Add(Number lhs, Number rhs)
    {
        return _numberConverter.Parse(lhs)
            + _numberConverter.Parse(rhs);
    }
}

The problem

.NET does not allow us to put F# code in a C# project. So we'll need to create a separate F# project. But this add a new problem: the Calculator will need to call the F# Add method of the NumberConverter class. However, the NumberConverter uses the Number class, which is defined in the C# project. This is at the heart of the circular reference problem.

Ordinarily (i.e. if this was an application, not a library), I would solve this problem by using a standard IoC container, such as Ninject. In the composition root of the application, we would wire up our dependencies (INumberConverter to NumberConverter). Then, when we needed something with dependencies, we'd ask the IoC container for it, and it would handle injecting the dependencies. But this won't work here, because we have a library, not an application. In a library, there is no composition root. So, we're stuck. Is there a way around this conumdrum? As luck would have it, there is.

The solution

To start, we'll create an interface, INumberConverter, in the C# project. Then we'll have the F# project reference the C# project. And we'll the NumberConverter class so that it inherits from INumberConverter. And we'll need to change our Calculator class too. We'll use Dependency Injection to inject an instance of INumberConverter into the Calculator.

The INumberConverter interface (in the C# project):

public interface INumberConverter
{
    double Parse(Number number);
}

The new NumberConverter class (in the F# project):

public class NumberConverter : INumberConverter
{
    // Pretend really hard that this is written in F#. :)
    public double Parse(Number number)
    {
        return double.Parse(number.Value);
    }
}

The Calculator class (in the C# project):

public class Calculator
{
    private readonly INumberConverter _numberConverter;

    public Calculator(INumberConverter numberConverter)
    {
        _numberConverter = numberConverter;
    }

    public double Add(Number lhs, Number rhs)
    {
        return _numberConverter.Parse(lhs)
            + _numberConverter.Parse(rhs);
    }
}

A new problem

This totally works, but we've eliminated the default constructor in Calculator. And that sucks. We really want that default constructor because we're making a reusable library and it is unreasonable to expect our library's consumers to have pass an inheritor of INumberConverter in to Calculator.

A new solution

We need to find a way to get that default constructor back in. What if Calculator had some magic way to get ahold of a NumberConverter? What would we do if we had that magic? We would probably have that default constructor pass the NumberConverter to the non-default constructor. Something like this:

public class Calculator
{
    private readonly INumberConverter _numberConverter;

    public Calculator(INumberConverter numberConverter)
    {
        _numberConverter = numberConverter;
    }

    public Calculator()
        : this(Magic.GetNumberConverter()) // <-- Magic!
    {
    }

    public double Add(Number lhs, Number rhs)
    {
        return _numberConverter.Parse(lhs)
            + _numberConverter.Parse(rhs);
    }
}

What if we made the GetNumberConverter method generic? Then we'd end up with Magic.Get<INumberConverter>(). And what if we renamed Magic to ServiceLocator? Well, then we'd have ServiceLocator.Get<INumberConvert>(), and we would suddenly be using the Service Locator pattern. Now, I'm not a huge fan of the Service Locator pattern. In fact, many people call it an anti-pattern. But in this case, I think it works. Especially if you declare the ServiceLocator as internal. Here's the general shape of the ServiceLocator class:

internal static class ServiceLocator
{
    public static T Get<T>()
    {
        return ??????????;
    }
}

The final piece of the puzzle

To solve the final piece of the puzzle, we're going to use Managed Extensibility Framework, or MEF. The reason why I chose MEF for this was because it's part of .NET's base class library (BCL). Because we're building a library, we don't want to force its consumers to use some third-party library. In other words, MEF is always available (at least to .NET 4.0 applications). The first thing we'll do is decorate the NumberConverter class with MEF's ExportAttribute, passing in typeof(INumberConverter. This tells MEF that we want an instance of NumberConverter to be returned when we ask MEF for a INumberConverter.

[Export(typeof(INumberConverter))] // <-- Add ExportAttribute
public class NumberConverter : INumberConverter
{
    // Pretend really hard that this is written in F#. :)
    public double Parse(Number number)
    {
        return double.Parse(number.Value);
    }
}

The final step is to tell the ServiceLocator to use MEF in order to be able to return an instance of INumberConverter. This is where MEF really shines. We can instruct MEF to scan the directory of the currently executing code for DLLs, and when it finds any, to scan the DLLs for types decorated with ExportAttribute. Without going into the details of how MEF does this, here is the final version of ServiceLocator:

internal static class ServiceLocator
{
    private static readonly CompositionContainer _container =
        new CompositionContainer(new DirectoryCatalog(@".\"));

    public static T Get<T>()
    {
        return _container.GetExportedValue<T>();
    }
}

Putting it all together

Let's create a simple console application to use the Calculator. We'll add references to both the C# project and the F# project. Then, all we need to do is instantiate a Calculator and pass in two Number objects.

static void Main()
{
    var firstNumber = new Number { Value = "123.456" };
    var secondNumber = new Number { Value = "654.321" };

    var calculator = new Calculator();
    var result = calculator.Add(firstNumber, secondNumber);

    Console.WriteLine(result);
    Console.ReadKey();
}

VoilĂ ! We have resolved the circular dependency problem, and the consumers of our library are none the wiser.

3 comments:

  1. Bheeshm Prakash NayakJune 12, 2013 at 6:38 AM

    Nice Article !!!. I have a question , that if i have a Solution that has many Class Libraries, and i need to solve Circular ref. problem in a particular Library , How do i know that Where i should put my Interface?
    E.g. In your Program, you know that Calculator is the Primary Point to enter into application... But How Do i know in my application? Should i consider the Build Order? Like whichever is building first put the interface there?

    ReplyDelete
  2. I following the Dependency Inversion Principle here, so I put interfaces in the projects that depend on them. You can see this in the example project - the Calculator depends on a INumberConverter, so INumberConverter goes in the same probject as Calculator. The actual implementation of INumberConverter is in a separate project, which has a reference to Calculator's project.



    A prerequisite for this approach is that all dependencies exist as interfaces. If your application is not coding to interfaces, this isn't going to work for you.

    ReplyDelete