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.
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?
ReplyDeleteE.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?
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.
ReplyDeleteA 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.
Great readd thankyou
ReplyDelete