Skip to content
This repository has been archived by the owner on Sep 16, 2023. It is now read-only.

ResolutionBestPractice

Craig Fowler edited this page Jan 17, 2018 · 5 revisions

Best practice in dependency injection is to allow the container to resolve your services recursively. Prefer the following technique over repeatedly using an IContainer or IResolvesServices to resolve many services, stitching them together by hand.

// Some classes we will be considering.
// We will ignore the interface definitions,
// they are not relevant to this example
public class MyTopLevelService : ITopLevelService
{
  public MyTopLevelService(IDependencyOne one, IDependencyTwo two)
  { /* Omitted: Do something with these dependencies */ }
}
public class DependencyOne : IDependencyOne
{
  public DependencyOne(INestedDependency nested)
  { /* Omitted: Do something with the dependency */ }
}
public class DependencyTwo : IDependencyTwo
{
  // No dependencies, parameterless constructor
}
public class NestedDependency : INestedDependency
{
  // No dependencies, parameterless constructor
}

// Now the registrations for these services.
container.AddRegistrations(helper => {
  helper.RegisterType<MyTopLevelService>()
      .As<ITopLevelService>();
  helper.RegisterType<DependencyOne>()
      .As<IDependencyOne>();
  helper.RegisterType<DependencyTwo>()
      .As<IDependencyTwo>();
  helper.RegisterType<NestedDependency>()
      .As<INestedDependency>();
});

// Resolving the top-level service
var myTopLevel = container.Resolve<ITopLevelService>();

In this example the class MyTopLevelService declares dependency upon IDependencyOne and IDependencyTwo by including them as parameters in its constructor. FlexDi will detect this and resolve instances of those dependencies in order to fulfil the resolution of the top-level service. When resolving IDependencyOne, it will use the class DependencyOne, which itself declares a dependency upon INestedDependency. This nested dependency will also be resolved automatically in order to satisfy DependencyOne.

This recursive resolution will continue as far as possible through the entire object graph of dependencies, until either:

  • FlexDi encounters a circular dependency (in which case it will usually raise an exception indicating as much)
  • FlexDi encounters a dependent service which it cannot resolve, for example a dependency upon an interface which is not registered
  • The entire object graph of dependencies is fulfilled, and resolution is successful

What not to do

Avoid resolving each individual service from the container and then stitching them together 'by hand'. Also, avoid including IContainer or IResolvesServices as constructor parameters to your services (then using those to resolve other services). These two practices lead you towards the direction of using the service locator anti-pattern.

In that second scenario (in which your service requires dynamic dependencies) you should consider a factory registration; this allows you to at least keep that logic for dependency selection within your registration (and thus DI) code, instead of spreading dependencies upon MicroDI into your application.

Clone this wiki locally