4

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.

Marcello
  • 879
  • 6
  • 20
  • It isnt very clear to me what it is you are asking here. I get the feeling you are missing the point of IoC / DI containers. By having everything in the CompositionRoot you then rely on abstractions (interfaces / abstract classes) and easily inject different behavior (via concrete impls.) – Wjdavis5 Apr 03 '16 at 14:26
  • My question is: where do you place your (tons of) container configuration rules where interfaces and implementations are spread throughout a solution composed of twenty projects? – Marcello Apr 03 '16 at 16:02
  • I guess it really depends on your project. For example I have about 50 projects in a sln for a very large service. But there is only a single exe. My rules are initialized as part of that exe starting up. If you are in a situation where there are many valid entry points you'll want to have your rules setup @ each entry point. Which shouldnt be that difficult if you you make EXE's class inherit from a common base class, that would then in turn contain the rules. – Wjdavis5 Apr 03 '16 at 17:48
  • Approach 2 works as long you're standardized on one DI container and plan to stay that way. That's likely and reasonable. If you're concerned about tying your project to one DI framework then you can just externalize that configuration in another assembly. That way you could have a Unity configuration, a Windsor configuration, etc. That matters more if you don't know who will be using your code. If you don't think it matters then it probably doesn't. – Scott Hannen Apr 03 '16 at 18:56

2 Answers2

3

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));
}
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • 1
    It seems very clean. Any concern about lifetimes? How does a module know what kind of lifetime (instance for web request vs singleton vs ...) is right for the top-level project (i.e. MyProject.Web.UI)? Is this a violation of responsibility we must accept in order to make things work? – Marcello Apr 04 '16 at 05:37
  • Yeah each type should know how long it should live, regardless of Web, Console, Service, Win32. For instance EF ContextDB shouldn't live forever in any application. If you populate a configuration class, it should be a singleton regardless of application. – Erik Philips Apr 04 '16 at 15:48
  • If I was adding unit tests and had an `InMemoryPersonRepository`, how would I swap out the `PersonRepository` for my `InMemoryPersonRepository`? – Andy T Apr 06 '16 at 20:42
  • 2
    For unit tests, I would not use a DI container at all. Dependencies should be mocked (e.g. with [moq](https://github.com/moq/moq4)) – Marcello Apr 10 '16 at 09:05
2

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.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • I suppose I should a "a solution," not "the solution." I'm sure there are other approaches. – Scott Hannen Apr 03 '16 at 18:50
  • If A references the container project, then A cannot expose interfaces, otherwise in order to register those interfaces a circular dependency would happen. Or would you rely on reflection? – Marcello Apr 04 '16 at 05:04
  • The container configuration project would be for dependencies within B that A shouldn't even know about at all. "A" shouldn't be exposing them. This is to hide inner workings of "B" and "C" from "A". (Remind me to never call anything A, B, and C ever again.) – Scott Hannen Apr 04 '16 at 18:58