Blog Archives

Inversion of Control and Dependency Injection (advanced, Part 1), Programming Patterns

I finally had some time and spent some hours on Inversion of Control (IoC) and Dependency Injection (DI). When I did the same a few weeks ago I did not understand a lot. I got lost on the concept of inversion. I tried to figure out, what the inversion was. This blocked all and I ended up pretty much blank despite nearly an hour of pumping information into my head. When you get lost then you should do it at least properly and at full steam šŸ˜‰  Well, I finally understood the headline … and the world started making sense again.

 

Why the name ā€œInversion of Controlā€? (IoC)

An entirely hardcoded class controls the program execution path. All branches are predetermined. By using interfaces you can decouple theses flows, so that at the time of creation the class does not exactly know what instructions to call next.
Let’s say you are using events. The class, which is raising an event, hands the control over to another class. The subscribing class is in control of the program flow, not the class that raises the event. The subscribing class can subscribe or unsubscribe. The class, which is providing the event, should not have any active control over subscriptions. Hence it is an inversion of control.
By using IoC classes become more encapsulated. Let’s say a perfect class is blind and urgently needs a guide dog. The control can now be taken over by external factors. This inverts the control entirely.
Unit testing uses that mechanism. A container can be used to control classes and change the bindings.

 

What is a container?

The expression ā€œcontainerā€ is rarely used in C#. The C++ world calls containers what C# calls collections. We have come across containers in my C++ posts. Follow the link for a quick refresher.
To simplify the matter we can call a UnityContainer a dictionary of objects with some additional methods.
This container performs binding between components. For instance it replaces all specific interface declarations by fully instantiated classes without the need to explicitly call any initialization.

 

What is dependency?

The structure of a program is: Input, Calculations, Output. The same generally applies to classes. Let’s say you want to run an analysis of a text file. That analysis class can only function properly if the required text file does exist. The calculations depend on the input. The same applies to the output, which can only run if the calculation was successful.

The input class calls the calculation class, which in turn calls the output class. As we are discussing inversion, let’s decouple the classes and implement events. In this case the output class subscribes to the calculation result event and the calculator subscribes to the input event.

 

What Dependency Injection? (DI)

“Dependency Injection” is a subset of “Inversion of Control”. IoC is a principle, whereas DI is an actual implementation of the principle.
DI is a software design pattern that allows removing hard-coded dependencies. First of all you do not create objects directly; you just describe how they look like. To accomplish this, the pattern uses interfaces or base classes. As the dependency is not hardcoded, the actual object can vary each time. It just needs to fulfill the inheritable pattern of an interface or base class.

public class Car { }   // base class
public class Audi : Car { }
public class BMW : Car { }
public class Exhibition {
   Car StageObject { set; get; }  // assign Audi or BMW as the dependency object of class Exhibition
}

 

Interfaces

What are the benefits of interfaces besides multiple inheritance? Several people can work on different problems simultaneously by using interfaces. One person for instance writes the code for logging to text files and another person writes the code for logging to databases. If both use the same interface definition, then the underlying classes can be easily replaced by each other. Interfaces are like promises to provide predefined class patterns at runtime.
Thus we end up with component separation, which is very useful in unit testing. Interfaces can eliminate unfathomable dependencies if used wisely.

 

Dependency Injection and Unit Testing

When you run unit tests, then you will need input data. But it could be too complex to run the entire program just to test some methods. You would try to only instantiate the minimum requirements. Think of a syringe and inject the data into the test case to create an acceptable environment. This can be difficult in case the program was not structured well. You need to examine the code to find dependencies.

Inversion of Control

 

I will cover some IoC/DI basics today and will follow-up on this after some other posts, which were queuing up in the last weeks:

  • Calling Java from C#
  • Calling C# from Java
  • Implement all Java source code examples of the 15 day series C# to C++
  • WPF Datagrid formatting
  • Google Authenticator
  • create a post index for my blog

 

Dependency Injection

There are several ways to implement dependencies in a class. The easiest way is to have a field that holds a reference to the dependency, which is probably the worst approach you can have in terms of flexibility.

public class Report1 {
 private IDataBase _DB = new DataBase();
} 

Report1 r1 = new Report1(); // dependency DataBase must be figured out by examining the code

 

You can use methods or properties to tell your classes what dependency objects they should use. The best approach though is via constructors. You can hardly miss parameters when trying to call the constructor. Of course bad constructor overloading can jeopardize this concept.

public class Car { }  // base class (instead of an interface)
public class Audi : Car { }
public class BMW : Car { }
public class Exhibition {
   Car StageObject { set; get; }  // assign Audi or BMW as the dependency object of class Exhibition
}

public class Report2 {
   public IDataBase DB { get; private set; }
   public void SetDB(IDataBase xDB) { DB = xDB; }
} 

public class Report3 {
  public IDataBase DB { get; set; }
} 

public class Report4 {
   private IDataBase _DB;
   public Report4(IDataBase xDB) { _DB = xDB; }
} 

DataBase lDB = new DataBase();
Report2 r2 = new Report2(); r2.SetDB(lDB);
Report3 r3 = new Report3(); r3.DB = lDB;
Report4 r4 = new Report4(lDB); 

 
If the world was just like class Report4, then we could more or less end the post here. Unfortunately dependencies are often not that obvious. They are well hidden and you need to read the code thoroughly to build unit tests.
Dependency Injection goes further and the real take off takes place with Moq, which I will explain in the follow-up post.

The following code example was didactically compiled. You don’t need any further information, it should be self-explanatory. You can download unity by following this link or using NuGet.

using System;
using Microsoft.Practices.Unity;

public interface IDataBase {
   void QuerySomething();
} // interface

public interface ITextFile {
   void LoadSomething();
} // interface

public interface INetwork {
   string Text { set; get; }
   void ReceiveSomething();
} // interface

public class Network : INetwork {
   public void ReceiveSomething() { Console.WriteLine("Receiving TCP data ..."); }
   public string Text { set; get; }
} // class

public class DataBase : IDataBase {
   private string _Dummy = "I am doing something.";
   public void QuerySomething() { Console.WriteLine(_Dummy); }
} // class

public class TextFile1 : ITextFile {
   public void LoadSomething() { Console.WriteLine("TF1: Loading something..."); }
} // class

public class TextFile2 : ITextFile {
   public void LoadSomething() { Console.WriteLine("TF2: Loading something..."); }
} // class

public class TextFile3 : ITextFile {
   public void LoadSomething() { Console.WriteLine("TF3: Loading something..."); }
} // class

public class Report5 {
   public string Text = "#N/A";
   private IDataBase _DB;
   public readonly ITextFile TF;
   public IDataBase getDB() { return _DB; }
   public Report5(IDataBase xDB, ITextFile xTextFile) { _DB = xDB; TF = xTextFile; }
} // class

public class Report6 {
   public readonly string Text1;
   public readonly string Text2;
   private readonly ITextFile _TextFile;
   public readonly INetwork Network;
   public Report6(ITextFile xTextFile, INetwork xNetwork, string xText1, string xText2) {
      _TextFile = xTextFile;
      Network = xNetwork;
      Text1 = xText1;
      Text2 = xText2;
   } // constructor
} // class

class Program {

   static void Main(string[] args) {

      UnityContainer lContainer = new UnityContainer(); // using Microsoft.Practices.Unity;  

      Report5 r;

      // insufficient data
      Console.WriteLine("test: insufficient data");
      Console.WriteLine("Registering IDataBase");
      lContainer.RegisterType(typeof(IDataBase), typeof(DataBase)); // whenever someone asks for an IDataBase, then return a new DataBase instance    
      try {
         r = lContainer.Resolve<Report5>(); // throws an exception, because ITextFile is undefined
      }
      catch (Exception ex) { Console.WriteLine(ex.Message); }
      Console.WriteLine();

      // full data
      Console.WriteLine("test: sufficient data");
      Console.WriteLine("Registering ITextFile TF1");
      Console.WriteLine("IDataBase is still registered");
      lContainer.RegisterType(typeof(ITextFile), typeof(TextFile1));
      r = lContainer.Resolve<Report5>();
      Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile1
      r.getDB().QuerySomething();
      r.TF.LoadSomething(); // this is TextFile1
      Console.WriteLine();

      // override a previous type registration with another type
      Console.WriteLine("test: override a previous type registration with another type");
      Console.WriteLine("Registering ITextFile TF2");
      lContainer.RegisterType(typeof(ITextFile), typeof(TextFile2)); // override the first type registration    
      //lContainer.RegisterType<ITextFile, TextFile2>();   // same as lContainer.RegisterType(typeof(ITextFile), typeof(TextFile2));
      r = lContainer.Resolve<Report5>();
      Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile2
      r.getDB().QuerySomething();
      r.TF.LoadSomething(); // this is TextFile2 
      Console.WriteLine();

      // override a previous type registration with an instance
      Console.WriteLine("test: override a previous type registration with an instance");
      Console.WriteLine("Registering an instance of TextFile3");
      ITextFile lTextFile = new TextFile3();
      lContainer.RegisterInstance(lTextFile);
      r = lContainer.Resolve<Report5>();
      Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile3
      r.getDB().QuerySomething();
      r.TF.LoadSomething(); // this is TextFile3 
      Console.WriteLine();

      // using names to register instances
      Console.WriteLine("test: using names to register instances");
      lContainer.RegisterType<Report5, Report5>(); // creates a default class without any name
      Report5 a = new Report5(r.getDB(), r.TF); lContainer.RegisterInstance("A", a); a.Text = "Report A";
      Report5 b = new Report5(r.getDB(), r.TF); lContainer.RegisterInstance("B", b); b.Text = "Report B";

      r = lContainer.Resolve<Report5>("A"); Console.WriteLine("got " + r.Text);
      r = lContainer.Resolve<Report5>("B"); Console.WriteLine("got " + r.Text);
      r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); r.Text = "same instance?";
      r = lContainer.Resolve<Report5>("X"); Console.WriteLine("got " + r.Text);  // new instance
      r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); // new instance
      Console.WriteLine();

      // => HAVE A LOOK AT THE BELOW CONTAINER SNAPSHOT => there are 3 instances for Report5

      // using names to register instances
      Console.WriteLine("test: revision, using the same instance");
      Console.WriteLine("ContainerControlledLifetimeManager: re-use instances (singleton behaviour for objects)");
      lContainer.RegisterType<Report5, Report5>(new ContainerControlledLifetimeManager());
      r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); r.Text = "same instance?";
      r = lContainer.Resolve<Report5>("X"); Console.WriteLine("got " + r.Text);  // new instance
      r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); // same instance !!!!!
      Console.WriteLine();

      // constructors with parameters
      lContainer.RegisterType<INetwork, Network>();
      lContainer.RegisterType<ITextFile, TextFile2>();
      Console.WriteLine("test: constructors with parameters");
      ResolverOverride[] lParameters = new ResolverOverride[] { new ParameterOverride("xText1", "Hello "), new ParameterOverride("xText2", "world") };
      Report6 lReport6 = lContainer.Resolve<Report6>(lParameters);
      Console.WriteLine("Report6 text field values are: " + lReport6.Text1 + lReport6.Text2);

      Console.ReadLine();
   } //

} // class

 

instances
 

example output:
test: insufficient data
Registering IDataBase
Resolution of the dependency failed, type = “Report5”, name = “(none)”.
Exception occurred while: while resolving.
Exception is: InvalidOperationException – The type ITextFile does not have an ac
cessible constructor.
———————————————–
At the time of the exception, the container was:

Resolving Report5,(none)
Resolving parameter “xTextFile” of constructor Report5(IDataBase xDB, ITextFil
e xTextFile)
Resolving ITextFile,(none)

test: sufficient data
Registering ITextFile TF1
IDataBase is still registered
type of r.TF is TextFile1
I am doing something.
TF1: Loading something…

test: override a previous type registration with another type
Registering ITextFile TF2
type of r.TF is TextFile2
I am doing something.
TF2: Loading something…

test: override a previous type registration with an instance
Registering an instance of TextFile3
type of r.TF is TextFile3
I am doing something.
TF3: Loading something…

test: using names to register instances
got Report A
got Report B
got #N/A
got #N/A
got #N/A

test: revision, using the same instance
ContainerControlledLifetimeManager: re-use instances (singleton behaviour for ob
jects)
got #N/A
got #N/A
got same instance?

test: constructors with parameters
Report6 text field values are: Hello world