Click here to Skip to main content
14,665,188 members
Articles » Development Lifecycle » Design and Architecture » General
Tip/Trick
Posted 21 Feb 2017

Tagged as

Stats

7.7K views
4 bookmarked

How Ninject Can Help in Resolving Circular Dependencies

Rate this:
5.00 (2 votes)
Please Sign up or sign in to vote.
5.00 (2 votes)
21 Feb 2017CPOL
A general technique is shown how to resolve circular dependencies with Ninject and a helper class

Introduction

Circular dependencies are a common problem in software engineering. In this tip, I'll explain why and I will explain how to solve it using Ninject and some helper class. When using this, exceptions can be prevented.

Background

To understand the code, some basic Ninject experience can be useful.

Using the Code

The concept of a circular dependency is very simple. There is ClassA that depends on a ClassB and requires an instance of ClassB to do its work. Therefore, giving an instance of ClassB as a constructor argument is obvious. However, the other way around is also possible. So which one should be created first? From a compile time perspective, the problem is easy to solve. If ClassA and ClassB are in different assemblies, your code will not compile. The compiler needs to build the assembly of ClassA first because classB needs it but the other way around is also possible. In this case, just use the GoF principle: "program to an interface, not an implementation". If both classes don't know each other directly, but they just know each others' interfaces, your compiler will not complain.

However, at runtime, there is still a problem. Which one to create first? They require each other at runtime anyway..... The concept of lazyness can help us. Here are our classes:

public class Class1 : IClass1
{
	private readonly Lazy<IClass2> objectTwo;
	public Class1(Lazy<IClass2> objectTwo)
	{
	    this.objectTwo = objectTwo;	
	}
	
	public int ReturnOne()
	{
	    return 1;
	}
	
	
	public void DoSomething()
	{
	    var value = this.objectTwo.Value.ReturnTwo();
	    Console.WriteLine($"{value}+{this.ReturnOne()}");
	}	
}

public class Class2 : IClass2
{
	private readonly Lazy<IClass1> objectOne ;
	public Class1(Lazy<IClass1> objectOne )
	{
    		this.objectOne = objectOne;
	}

	public int ReturnTwo()
	{
   	 	return 2;
	}


	public void DoSomething()
	{
    		var value = this.objectTwo.Value.ReturnTwo();
    		Console.WriteLine($"{value}+{this.ReturnOne()}");
	}
}

Here, you can to see how lazyness solves the problem. You can use the constructor based dependency injection. You don't need a setter method that you need to call afterwards and that can be recalled (which is in many cases what you don't want). The instance that is created at first, is just the instance of the class that is used first. You don't need to worry about it,. Once an instance is needed, it exists.

This is our Ninject Module.

public class MyModule : NinjectModule
{
	public override void Load()
	{	
	    this.Bind<IClass1>().To<Class1>();
	    this.Bind<IClass2>().To<Class2>();
	    this.BindLazy<IClass1>().To<Class1>();
	    this.BindLazy<IClass2>().To<Class2>();	
	}
}

As you can see, the code in our Ninject Module is very easy to use. In addition, to Bind, you use a "BindLazy". Problem solved, no setters needed, no extra properties needed and no need to worry about what to create first.

To ensure "BindLazy" works, add the following code:

public static class NinjectExtensions
{
	public class LazyCombi<T>
	{
		public IBindingToSyntax<Lazy<T>> LazyBindingSyntax { get; private set; }
		public NinjectModule Module { get; private set; }
		
		public LazyCombi(NinjectModule module, 
		IBindingToSyntax<Lazy<T>> lazyBindingSyntax)
		{
			this.Module = module;
			this.LazyBindingSyntax = lazyBindingSyntax;
		}
		
           	public IBindingWhenInNamedWithOrOnSyntax<Lazy<T>> To<TImplementation>() 
                   where TImplementation : T
		{
			return this.LazyBindingSyntax.ToMethod(c =>
			{
				return new Lazy<T>(() =>
				{
					return this.Module.Kernel.Get<TImplementation>();
				}
				
			});
		}
		
		public static LazyCombi<T> BindLazy<T>(this NinjectModule module)
		{
			return new LazyCombi<T>(module, module.Bind<Lazy<T>>());		
		} 
	}	
}

Points of Interest

The problem is well known and I found out that a lot of people use setter methods or properties for this problem. However, dependency injection with setters is to modify the dependency. Sometimes, that is useful but in many cases, you want to ensure that it is injected once. That constructor based dependency injection is better and with lazyness, this is also possible for circular dependencies. Using a setter has disadvantages that can be avoided with constructor based dependency injection. There is no reason to make your object mutable, if you don't want to modify it and you prefer your application to be thread safe, please avoid setters unless you are sure you need a mutable state, otherwise setters are just redundant code.

To test the Ninject Module:

static void Main(string[] args)
{
	var firstKernel = new StandardKernel(new MyModule());
	var objectOne = firstKernel.Get<IClass1>();
	objectOne.DoSomething();
	var secondKernel = new StandardKernel(new MyModule());
	var objectTwo = secondKernel.Get<IClass2>();
	objectTwo.DoSomething()
	Console.ReadLine();
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Daan Acohen
Software Developer (Senior)
Netherlands Netherlands
I am a self-employed software engineer working on .NET Core. I love TDD.

Comments and Discussions

 
QuestionNice concept, but Pin
Dupsi Yupsi21-Feb-17 22:55
MemberDupsi Yupsi21-Feb-17 22:55 
AnswerRe: Nice concept, but Pin
Daan Acohen22-Feb-17 0:01
MemberDaan Acohen22-Feb-17 0:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.