Grace: The perfect DI IoC container [part 1] 💻🤓

GRACE

 

Dependency Inversion Principle

We’re in 2020 and you really need to know a few programming principles if you want to be a winner. One of the most important is the Dependency Inversion principle. It says that we should get our dependencies from the outside. What does it mean? Let’s see it with one example. Imagine that you have a beautiful class that represents some algorithm:

 

class Algorithm
{
	public int DoIt(int[] input)
	{
		var serviceA = new ServiceA();
		var serviceB = new ServiceB();
		…
	}
}

 

… you’re going to get into some serious troubles when you’re trying to test it. Moreover, you’ll be having a hard time if the services A and B change their constructors because you’ll have to supply the new parameters in every place they’re created! Moreover, you’re coding against implementations of your dependencies instead of interfaces, so dependencies won’t be replaced easily.

There’s a better way to implement the Algorithm class. How about this?

class Algorithm
{
	private IServiceA serviceA;
	private IServiceB serviceB;

	public Algorithm(IServiceA serviceA, IServiceB serviceB)
	{
		this.serviceA = serviceA;
		this.serviceB = serviceB;
	}

	public int DoIt(int[] input)
	{
		…
	}
}

We simply moved the responsibility of newing up the dependencies (the services) to the caller, whoever the caller is, and that we now rely on interfaces instead of concrete implementations.

This allows for a lot of -ability words that will grant you glory and future satisfaction with your code. If you’re unfamiliar with this approach, you should start reading about the Dependency Inversion Principle right now 😊, in addition to the rest of the SOLID principles.

Containers

There are some ways to put principles into practice. One of the most popular is to use a software artifact called DI IoC container (Dependency Injection/Inversion of Control container).

With this approach, the container acts like a “magic box”. You tell it “hey, give me an instance of this type”, and the container is smart enough to provide you with an instance of that type.

The interesting thing is that the container will just know how to locate those instances. Sometimes it will reuse existing instances. Sometimes it will create new instances. The interesting and important things is that you don’t have to create anything. It will do.

In order to get the advantage of a container, you’re supposed to design your classes well, and this involves applying the DI principle shown above.

How do I use DI IoC containers

Most containers are similar in terms of the basic usage. You usually register types with the container and later, you locate those types.

Container registration

For the container to work, you need to configure it. This is often called “registration”.

The most basic registration is Interface to Type registration:

Let’s see it with an example:

container.Export<MyService>().As<IMyService>();

This is equivalent to saying “whenever you need IMyService, use the MyService implementation”

From now on, the container will use the given implementation when it’s asked for that interface.

Container location

Once the registration is done, we can ask the container:

var myService = container.Locate<IMyService>();

The container will provide an instance of MyService, because it’s configured to do so.

It looks too easy, doesn’t it?

Let’s see a more complex example:

class One : IOne
{
	public One(IAnother another)
	{
		…
	}
}

class Another : IAnother
{
	public Another()
	{
		…
	}
}

Now, One depends on Another.

Let’s configure our container

container.Export<One>().As<IOne>();
container.Export<Another>().As<IAnother>();

Finally, we are going to use the container to “locate” the type IOne

var one = container.Locate<IOne>();

You ask for an instance of One, but to create One, we need an instance implementing IAnother!

However, the container is smart and will analyze how instances are created and will supply the constructor parameters according to the registration (configuration).

Analyzing how the container works

Consider this call:

container.Locate<IOne>();

From the point of view of the container, we do the following:

As things get complex, the container becomes more and more useful because you only register the times you can to make it aware of, and associated interfaces to implementations.

So, from your programming point of view, you just design your classes making dependencies explicit (in the constructor) and later, the container will supply the dependencies automatically for you.

This is only the beginning. If you want to become a master with dependency inversion, there’s a very good book on the matter: Dependency Injection in .NET, by Mark Seemann.

There are a lots of containers out there that you can use to max out the DI principle, but there’s one that excels at it’s goal, and it’s Grace.

Meet Grace

Grace is the DI IoC container of our choice for a lot of reasons:

This is how it’s configured and how a locate call look.

using Grace.DependencyInjection;

var container = new DependencyInjectionContainer();
container.Configure(c => c.Export<BasicService>().As<IBasicService>());
var basicService = container.Locate<IBasicService>();

It’s as easy as create, configure, and locate.

It has an extensive set of useful features:

What’s next

In the next part of our article, we’ll show some of those wonderful features with working code and examples.

In the meanwhile, you can play around with it. Basic stuff is easy to do and it has nice documentation.

 

Useful links

Grace GitHub site

Author: Ian Johnson

 

Written by: Jose Manuel Nieto, part of Idiwork’s team.

Stay up to date!



Leave a comment