Simple Dependency Injection in C#

This time, I would like to introduce you the Dependency Injection (DI), which is a very common principle in programming.

Dependency Injection

Dependency Injection (DI) is one of many types of IoC implementation, where object creation is done outside of its parent class and then it’s injected there in some way:

  • Constructor Injection
  • Interface Injection
  • Setter Injection

IoC Container

First of all, we will need to install the IoC Container. For this purpose, I chose Autofac, which can be used in .NET Core, ASP.NET Core, .NET 4.5.1+, Universal Windows apps, Xamarin and more. In this step, just install Autofac Nuget Package into your Windows or Xamarin application.

IoC Builder

Now, we will implement a container builder, where we specify which types will be automatically resolved and in which way. To simplify the process of type registration, we can create interfaces, which will specify whether its objects should be resolved automatically. Many times we need to work with a single instance of an object, so we can create another interface for objects, that will always have injected the same instance.

public interface IResolvable { }
public interface ISingleResolvable { }

IoC Builder class will be used to build a container and register all classes from application assembly that implements our interfaces IResolvable or ISingleResolvable.

public class IoCBuilder
{
    private ContainerBuilder _builder = new ContainerBuilder();

    public IContainer BuildContainer()
    {
        _builder = new ContainerBuilder();

        RegisterTypes();

        return _builder.Build();
    }

    public virtual void RegisterTypes()
    {
        Register<IResolvable>();
        RegisterSingle<ISingleResolvable>();
    }

    public void Register<T>()
    {
        var baseType = typeof(T).GetTypeInfo();
        _builder.RegisterAssemblyTypes(baseType.Assembly)
            .Where(t => t.IsAssignableTo<T>() && !t.IsAssignableTo<ISingleResolvable>())
            .PropertiesAutowired();
    }

    public void RegisterSingle<T>()
    {
        var baseType = typeof(T).GetTypeInfo();
        _builder.RegisterAssemblyTypes(baseType.Assembly)
            .Where(t => t.IsAssignableTo<T>()).SingleInstance()
            .PropertiesAutowired();
    }
}

MVVM Locator

The last, but not least step is creating an MVVM Locator. MVVM Locator will use our IoC Builder to create a new container and resolve ViewModel when it’s needed by calling a generic method Resolve<T>.

public class MVVMLocator
{
    private IContainer _container;

    public MVVMLocator()
    {
        _container = new IoCBuilder().BuildContainer();
    }

    public T Resolve<T>()
    {
        return _container.Resolve<T>();
    }
}

..and create an instance in the application’s main class.

sealed partial class App : Application
{
    public MVVMLocator MVVMLocator = new MVVMLocator();
    
    ...
}

Resolving ViewModels

If we want our ViewModels to automatically resolve all dependencies, implement the IResolvable interface in ViewModel’s base class.

public class ViewModelBase : IResolvable, INotifyPropertyChanged
{ ... }

In code-behind file of a view, resolve an instance of appropriate ViewModel class.

public sealed partial class MainView : Page
{
    public MainViewModel ViewModel = ((App)Application.Current).MVVMLocator.Resolve<MainViewModel>();
    
    ...
}

Dependency Injection usage

Now, when ViewModel is automatically resolved, we need to understand the options of dependency injection.

Constructor injection

When classes implementing IResolvable or ISingleResolvable are added to a constructor of a ViewModel as parameters, they will be automatically resolved and we will get an instance that was built outside of the ViewModel.

public class ConstructorInjectionViewModel : ViewModelBase
{
    private Resolvable _resolvable;

    public SecondViewModel(Resolvable resolvable)
    {
        _resolvable = resolvable;
    }
}

Interface injection

Even more simple one is an Interface injection, where class implementing a resolvable interface is added to a ViewModel as public property. Sure, according to IoC Builder created earlier, we are not fulfilling the Dependency Inversion Principle because we are not creating resolvable interfaces for each service that should be resolved. However, for the purposes of simple mobile application, it’s sufficient and better testable.

public class MainViewModel : ViewModelBase
{
    public Resolvable Resolvable { get; set; }
}

Conclusion & Next steps

Using Dependency Injection we can avoid usage of static and singleton classes that can cause you some troubles.

Furthermore, to make the dependencies more testable and less coupled, you can register your dependencies manually using interfaces and take advantage of Dependency Inversion Principle (DIP).

DIP is based on the idea that the implementation of the Car class should not depend on the Engine class, but on the IEngine interface. If so, Engine can be simply changed for another implementation without any change in the Car class.

Hope it helps 🙂

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *