One of the things to watch out for when working with DI Containers is being aware of the Dependency Graph. One of the more dangerous types is where you expect a particular object to exist once during an entire HTTP Request lifecycle, but it starts behaving like a singleton.

Unfortunately, this is probably means, you have a broken dependency graph, as explained by Mark Seeman in his write-up of Captive Dependency.

This issue becomes introduces an even more critical bug when that said Transient dependency that you want is something as important as a Unit of Work, but it doesn’t quite behave as you expected.

As an example, you may have B that is dependent on A, where B is a Singleton, and B is Transient.

Let’s have a look at some example in aspnetcore to illustrate the issue further.

Services:

public class AService : IAService
{
    public AService()
    {
        Console.WriteLine("**** A SERVICE ****");
    }

    public void ADoSomething()
    {
        Console.WriteLine("ADoSomething");
    }
}

public class BService : IBService
{
    private readonly IAService _aService;

    public BService(IAService aService)
    {
        Console.WriteLine("**** B SERVICE ****");
        _aService = aService;
    }

    public void BDoSomething()
    {
        Console.WriteLine("BDoSomething");
    }
}

public interface IAService
{
    void ADoSomething();
}

public interface IBService
{
    void BDoSomething();
}

Controller:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly IBService _bService;

    public ValuesController(IBService bService)
    {
        _bService = bService;
    }

    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}

Startup

container.Configure(x =>
{
    x.For<IAService>().Use<AService>().Transient();
    x.For<IBService>().Use<BService>().Singleton();
});

You can grab the example from github.

Now, when you run this application and hit /api/values, this initially results in instantiating both A and B, in that order.

Application started. Press Ctrl+C to shut down.
**** A SERVICE ****
**** B SERVICE ****

Followed by nothing being instiatated, for every request here on.

The bug may be simple to spot in this trivial example, but this becomes a lot trickier to spot in production code with much more complex dependency graphs built using abstractions.

It’s best to keep your abstractions minimal for building your dependency graph and avoid too many dependencies.