Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where dependency-injection registrations have to be put?

I've read the question Ioc/DI - Why do I have to reference all layers/assemblies in application's entry point?

So, in a Asp.Net MVC5 solution, the composition root is in the MVC5 project (and having a DependencyInjection assembly in charge of all the registrations does not make sense).

Within this picture, it is not clear to me what is the better approach among the following.

Approach 1

The concrete implementations are public class ... and all registrations clauses are centralized within the composition root (e.g. in one or more files under a CompositionRoot folder). MVC5 project must reference all the assemblies providing at least one concrete implementation to be bound. No library references the DI library. MVC project can contain interfaces to be bound with no drawbacks.

Approach 2

The concrete implementations are internal class .... Each library exposes a DI 'local' configuration handler. For example

public class DependencyInjectionConfig {
    public static void Configure(Container container) {
        //here registration of assembly-provided implementations
        //...
    }
}

which is up to register its own implementations. The composition root triggers registrations by calling all the Configure() methods, just one for each project. MVC5 project must then reference all the assemblies providing at least one concrete implementation to be bound. Libraries must reference the DI library. In this case, the MVC5 project cannot contain interfaces (otherwise there would be a circular reference): a ServiceLayer assembly would be needed to hold public interfaces to be bound.

Approach 3

Same as Approach 2, but local configuration modules are discovered dynamically through assembly reflection (by convention?). So MVC5 project has not to reference libraries. MVC project can contain interfaces and can be referenced by libraries. Libraries must reference the DI library.

What is the best practice here? Is there some other better possibility?

EDIT 1 (2016-12-22) Thanks to received answers, I published this github project describing the best solution I found so far.

EDIT 2 (2018-09-09) This answer provides an interesting option.

EDIT 3 (2020-12-29) Finally, I came up with a complete solution, packaged in the form of a WebApi application template. I published this solution on GitHub HERE. This approach, not only gives a clear understanding about where DI rules have to be put, but also suggests to setup the application according to SOLID principles and CQRS pattern. The commit history of this project has been structured to have educational purposes.

EDIT 4 (2023-01-31) The repository linked above publishes an article describing the solution as well.

like image 787
Marcello Avatar asked Nov 04 '25 02:11

Marcello


2 Answers

I typically like to encapsulate these types of things into each project. So for example I might have the following. (This is an extremely simplified example, and I'll use the AutoFac in this example, but I'd imagine all DI frameworks have something like the following).

Common area for just POCOs and Interfaces.

// MyProject.Data.csproj
namespace MyProject.Data
{
  public Interface IPersonRepository
  {
    Person Get();
  }

  public class Person
  {
  }
}

Implementation of Repositories and Data Access

// MyProject.Data.EF.csproj
// This project uses EF to implement that data
namespace MyProject.Data.EF
{
  // internal, because I don't want anyone to actually create this class
  internal class PersonRepository : IPersonRepository
  {
    Person Get()
    {  // implementation  }
  }

  public class Registration : Autofac.Module
  {
    protected override void Load(ContainerBuilder builder)
    {
      builder.Register<PersonRepository>()
        .As<IPersonRepository>()
        .IntancePerLifetimeScope();
    }
  }
}

Consumer

// MyPrject.Web.UI.csproj
// This project requires an IPersonRepository
namespace MyProject.Web.UI
{
  // Asp.Net MVC Example
  internal class IoCConfig
  {
    public static void Start()
    {
      var builder = new ContainerBuilder();

      var assemblies = BuildManager.GetReferencedAssemblies()
        .Cast<Assembly>();

      builder.RegisterAssemblyModules(assemblies);
    }
  }
}

So the Dependencies look like:

MyProject.Data.csproj 
- None

MyProject.Data.EF.csproj 
- MyProject.Data

MyProject.Web.UI.csproj 
- MyProject.Data
- MyProject.Data.EF

In this setup, the Web.UI cannot know anything about what is registered nor for what reason. It only knows that the EF project has implementations but can't access them.

I can drop EF for say Dapper extremely easily as each project encapsulates it's own implementations and registration.

If I was adding unit tests and had an InMemoryPersonRepository, how would I swap out the PersonRepository for my InMemoryPersonRepository?

Assuming we ignore any business logic layer and have an MVC Controller directly access our Data Accessor, my code might look like:

public class MyController
{
  private readonly IPersonRepository _repo;

  public MyController(IPersonRepository repo)
  {
    _repo = repo;
  }

  public IActionResult Index()
  {
    var person = _repo.Get();

    var model = Map<PersonVM>(person);

    return View(model);
  }
}

Then a test using nSubstitute Might look like:

public class MyControllerTests
{
  public void Index_Executed_ReturnsObjectWithSameId
  {
    // Assign
    var repo = Substitute.For<IPersonRepository>();
    var expectedId = 1;
    repo.Get().Returns(new Person { Id = expected });
    var controller = new MyController(repo);

    // Act
    var result = controller.Index() as ActionResult<PersonVM>;

    // Assert
    Assert.That(expectedId, Is.EqualTo(result.Value.Id));
}
like image 168
Erik Philips Avatar answered Nov 06 '25 16:11

Erik Philips


You've identified a real problem. (One could say it's a good problem to have.) If entry application Areferences B, B references C, and B and/or C require some DI registration, that makes A (your entry application) responsible for knowing enough about the details of B and C to register all the dependencies.

The solution is to have a separate assembly that handles composing all of the registrations for B and C. A references that, and it provides all of the container configuration that A needs to use B and C.

The benefits are

  • A doesn't know more about B and C than it should
  • Neither A, B, nor C have to be tied to one particular DI framework like Unity or Windsor.

Here's an example. This is an event bus class that works best with a DI container. But in order to use it you shouldn't have to know all about the dependencies it needs to register. So for Windsor I created a DomainEventFacility. You just call

_container.AddFacility<DomainEventFacility>();

and all of the dependencies are registered. The only thing you register are your event handlers.

Then if I want to use the same event bus library with a different DI container like Unity I can just create some similar assembly to handle the same configuration for Unity.

like image 26
Scott Hannen Avatar answered Nov 06 '25 17:11

Scott Hannen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!