0

I am developing a class library that uses dependency injection and inversion of control.

As I understand it, the ideal IoC setup has only a single composition root in the main application. The library should register its dependencies with the root, so I'd like to expose a call that allows the main program to do this. The call might look like this:

void RegisterDependencies(Container container)
{
   ...

and be called like this:

//In the main program (which we are not writing)
static Container CompositionRoot()
{
    var builder = new ContainerBuilder();

    //Main program registers its own dependencies
    builder.RegisterType<IMainProgramType, MainProgramClass>();

    //Main program calls library to register the library's dependencies
    MyLibrary.Root.RegisterDependencies(builder);  //<<<<< the call I want to enable

    return builder.Builder();
}

The problem with the above is that the RegisterDependencies call must accept a parameter that contains the IoC container or some object that is used to build it. But the team writing the library doesn't know what container the main program is going to use, and we don't want to impose a limitation on it.

How to solve this problem? Is there a common interface we could expose, e.g.

//In my library
void RegisterDepencendies(IStandardContainer container)

Or perhaps expose it as a delegate?

//In my library
void RegisterDependencies(Action<object,object> registerAction)
{
    registerAction(typeof(IMyLibraryInterface), typeof(MyLibraryClass));
}

//In main program
MyLibrary.Root.RegisterDependencies( i,c => builder.RegisterType(i,c) );

Or should we abandon this approach and allow the library to have its own composition root and its own container?

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • 1
    Have the class library expose type mappings. the composition root could use those to build the graphs. – Nkosi Sep 25 '18 at 23:22
  • Taking a page out of .net-core DI you can expose your own version of a [ServiceDescriptor](https://github.com/aspnet/DependencyInjection/blob/master/src/DI.Abstractions/ServiceDescriptor.cs) – Nkosi Sep 25 '18 at 23:39
  • I would completely omit registration code from a class library. Typically, a library provides types (classes, interfaces etc.) to be used by other libraries or the executable. The DI container registration is part of the startup of the program, because that links all together and knows which libraries and types are used. And of course, which DI framework. Additionally, you can use the Microsoft CommonServiceLocator to hide the used DI framework. – KBO Sep 26 '18 at 06:22
  • @KBO I am not sure what you mean "skip registration code completely." Are you saying that whoever ends up using my library will need to figure out how to inject all of its dependencies themselves? – John Wu Sep 26 '18 at 07:12
  • I think the determining factor that determines the correct answer to this question is: are you building a reusable library (i.e. typically something that is distributed through NuGet) or are you building a project that is only shared within the same solution? – Steven Sep 26 '18 at 07:33
  • As Steven and Eric (below) said, it depends. By default, you have your library with interfaces and classes without DI framework dependencies. Then, you may add one or more libraries with the specific registration code of the DI frameworks, you want to support. So, the user of your library can decide, which DI framework or which version is used. And the user may also implement it's own registration for the DI framework, that is not supported by you. – KBO Sep 26 '18 at 12:18

1 Answers1

1

You could have your class library simply define the relevant types without referencing any specific IoC library and have one or more packages that target specific IoC frameworks with that framework's conventions for defining default registrations.

I.E. your project structure would look like:

  • MyLibrary (no dependencies)
  • MyLibrary.Autofac (depends on MyLibrary and Autofac)
  • MyLibrary.AspNetCore (depends on MyLibrary and Microsoft.Extensions.DependencyInjection)
Eric Damtoft
  • 1,353
  • 7
  • 13