Lazy Dependency Injection for .NET
Tags: dotnet, ioc, di, lazy
Published: 2020-11-21 01:54:57

# Dependency Injection Problem

Dependency injection is an essential part of modern applications that allows getting clean, reusable, testable code. Loosely coupled code and the single responsibility principle are considered best practices. And that's exactly what dependency injection helps to achieve.

Along with the power and benefits of dependency injection, there is at least one problem that can be illustrated as follows.

Assume a service called `MyService` depends on `ServiceA` and `ServiceB`. The number of all nested dependencies is `N` for `ServiceA` and `M` for `ServiceB`:

![2](https://dev-to-uploads.s3.amazonaws.com/i/2moiqz0c17300691no5s.png) 

All these services are registered in an IoC container by corresponding interfaces:

```CSharp
container
    .RegisterType<IMyService, MyService>()
    .RegisterType<IServiceA, ServiceA>()
    .RegisterType<IServiceB, ServiceB>()
    ...
```

The `MyService` service has the following implementation:

```CSharp
public class MyService : IMyService
{
    private readonly IServiceA _serviceA;
    private readonly IServiceB _serviceB;

    public MyService(IServiceA serviceA, IServiceB serviceB)
    {
        _serviceA = serviceA;
        _serviceB = serviceB;
    }

    public void DoWork(int value)
    {
        if (value < 42)
        {
            _serviceA.DoWork();
        }
        else
        {
            _serviceB.DoWork();
        }
    }
}
```

The `DoWork` method uses either `ServiceA` or `ServiceB` depending on the `value` parameter. The important thing is that the method does not simultaneously use both services, but only one of them.

Consider this example of using the `MyService` service:

```CSharp
public void MyMethod()
{
    // Using the container directly is for clarity only.
    var service = container.Resolve<IMyService>();
    service.DoWork(1);
}
```

In this case, only `ServiceA` needs to be used. However, when resolving `MyService` from the container, both `ServiceA` and `ServiceB` will be created as well as all other nested dependencies. Thus, instead of instantiating `(1 + N)` services, all `(2 + N + M)` are created.

The following example illustrates another case where only part of the dependencies is used:

```CSharp
public class MyService : IMyService
{
    private readonly IServiceA _serviceA;
    private readonly IServiceB _serviceB;

    public MyService(IServiceA serviceA, IServiceB serviceB)
    {
        _serviceA = serviceA;
        _serviceB = serviceB;
    }

    public void DoWorkA()
    {
        _serviceA.DoWork();
    }

    public void DoWorkB()
    {
        _serviceB.DoWork();
    }
}
```
When calling the `DoWorkA` method, only `ServiceA` is used. `ServiceB` is not needed in this case, but it was still created when the `MyService` service resolving from the container:

```CSharp
public void MyMethod()
{
    // Using the container directly is for clarity only.
    var service = container.Resolve<IMyService>();
    service.DoWorkA();
}
```

Of course, the examples given are too simple for real life and are given only for clarity. However, in practice, often, similar situations arise.

To summarize, the problem with dependency injection is that it creates multiple instances that are not even used when the method is called. It leads to: 

* Increased consumption of CPU time to create unused service instances. Especially important if for some reason there are slow constructors in those services.
* Increased memory consumption due to allocation for unused service instances.
* Decreased performance due to increased load on the garbage collector.

# Solutions

This problem can be solved in several ways.

### Singletons

The specified problem becomes negligible if the services are registered in an IoC container as singletons. In this case, the problem only affects the first resolution of the service. However, in many cases, making all services singletons is a rather problematic task, especially for legacy code.

### More Dependencies

In some cases, the service can be divided into dependencies by reducing responsibility and methods. For instance, in the second example above, the `DoWorkA` method and the `DoWorkB` method could be divided into different services. But this approach may not always help, as can be seen from the first example above when the `DoWork` method uses a condition by `value`.

### Fewer Dependencies

Sometimes, combining several services into one can reduce the depth of the dependency tree nesting. It is especially helpful when services are overly segregated. However, this can negatively impact code reusability and testability.

### ServiceLocator

Instead of injecting services, some use different service locators. However, this is considered to be an [anti-pattern that breaks encapsulation](https://blog.ploeh.dk/2015/10/26/service-locator-violates-encapsulation/), so ideally you want to avoid it.

### Inject Lazy&lt;T&gt; Instead of T

Using `Lazy<T>` rather than the `T` solves the indicated problem.

The implementation of the first example changes as follows:

```CSharp
public class MyService : IMyService
{
    private readonly Lazy<IServiceA> _serviceA;
    private readonly Lazy<IServiceB> _serviceB;

    public MyService(Lazy<IServiceA> serviceA, Lazy<IServiceB> serviceB)
    {
        _serviceA = serviceA;
        _serviceB = serviceB;
    }

    public void DoWork(int value)
    {
        if (value < 42)
        {
            _serviceA.Value.DoWork();
        }
        else
  {
            _serviceB.Value.DoWork();
        }
    }
}
```

In this case, two `Lazy<T>` instances are created when the `MyService` service is resolved. `(1 + N)` more services are resolved only when the `DoWork` method is executed with the `value == 1` (as in the example above). In total, instead of `(2 + N + M)` created instances, only `(3 + N)` are created.

The difference is more significant, the more dependencies the `MyService` service has, and method branches use the more specific services.

The downside of this approach is that it pollutes the code with `Lazy<T>` and `someServiceLazy.Value` making it less readable.

# LazyProxy

[LazyProxy](https://github.com/servicetitan/lazy-proxy) solves the above problem without requiring you to change your service code at all.

This library allows you to generate at runtime a type that implements a given interface and proxies all members of this interface to calls through `Lazy<T>`.

The following example shows how `LazyProxy` works.

Assume there is the following interface:

```CSharp
public interface IMyService
{
    void Foo();
}
```

Then a lazy proxy type can be generated this way:

```CSharp
var proxyType = LazyProxyBuilder.GetType<IMyService>();
```

The generated type looks like this:

```CSharp
// In reality, the implementation is a little more complicated,
// but the details are omitted for ease of understanding.
public class LazyProxyImpl_IMyService : IMyService
{
    private Lazy<IMyService> _service;

    public LazyProxyImpl_IMyService(Lazy<IMyService> service)
    {
        _service = service;
    }

    public void Foo() => _service.Value.Foo();
}
```

The generated type hides all of the boilerplate code of using `Lazy<T>`. So,  `LazyProxy` can eliminate the main disadvantage of lazy injection but keep the main advantage.

# LazyProxy Registration

Generating a proxy type at runtime is only half the battle. It is necessary to register this type in the container somehow and preserve the ability to resolve a real type when calling `_service.Value`. This can be achieved in several ways.

### Named registrations

Named registrations can be used to register and resolve a real service. This method can only be used for containers that support named registrations.

Here is a simple example of how a proxy type and a real type can be registered in the `Autofac` container:

```CSharp
const string registrationName = "RealService";

// Creating a container builder.
var builder = new ContainerBuilder();

// Registering a type mapping for the real service.
builder
    .RegisterType<MyService>()
    .Named<IMyService>(registrationName);

// Registering a type mapping for the lazy proxy.
builder
    .Register(c =>
    {
        var context = c.Resolve<IComponentContext>();
        return LazyProxyBuilder.CreateInstance(
            () => context.ResolveNamed<IMyService>(registrationName));
    })
    .As<IMyService>();

// Building the container.
using var container = builder.Build();

// Resolving the lazy proxy.
var lazyProxy = container.Resolve<IMyService>();
```

### Marker interface

Another way is generating an additional marker interface at runtime that will implement the main interface and be used to register and resolve a real service.

This method is a little more complicated to implement than named registrations, as it requires a runtime code generation. Simultaneously, this method is universal, as it can work for containers that do not support named registrations.

# LazyProxy for IoC Containers

There are already ready-made libraries for some IoC containers that implement the described approach.

### LazyProxy.Autofac

[LazyProxy.Autofac](https://github.com/servicetitan/lazy-proxy-autofac) implements lazy registrations for the `Autofac` container.

Here is an example of how to use this library:

```CSharp
var builder = new ContainerBuilder();
builder.RegisterLazy<IFoo, Foo>();
using var container = builder.Build();
var lazyProxy = container.Resolve<IFoo>();
```

### LazyProxy.Unity

There is also an implementation for the `Unity` container named [LazyProxy.Unity](https://github.com/servicetitan/lazy-proxy-unity).

The syntax is similar to the previous example:

```CSharp
using var container = new UnityContainer();
container.RegisterLazy<IFoo, Foo>();
var lazyProxy = container.Resolve<IFoo>();
```

# Conclusion

Dependency Injection is a powerful tool and standard for achieving quality code. However, it has the disadvantage that multiple instances are unnecessarily created. It negatively affects performance and memory consumption.

There are several solutions that you should use whenever possible to mitigate the negative impact of dependency injection.

One solution is using the `Lazy<T>` injection instead of `T`. However, such injection pollutes the code, so this use is not recommended.

Instead, it is suggested to use the `LazyProxy` library, which allows you to get lazy injection benefits while still getting clean code. This library can be used for various containers to register lazy dependencies. There are implementations for the `Unity` and the `Autofac` containers that allow you to simply register lazy dependencies.

# GitHub Repositories

* [LazyProxy](https://github.com/servicetitan/lazy-proxy)
* [LazyProxy.Autofac](https://github.com/servicetitan/lazy-proxy-autofac)
* [LazyProxy.Unity](https://github.com/servicetitan/lazy-proxy-unity)