1

I am using IoC container to register and resolve types.

I have the following Projects:

ProjectA

ClassA - SubClass1 - SubClass2

ProjectB(Class Library)

ClassB - SubClass3 - SubClass4

ProjcetA is using ProjectB.

ProjectA is the entry point to the application.

ProjectA doesn´t know about SubClasses3 and SubClass4.

I have separate unit test projects for ProjectA and ProjectB.

Since I don´t want ProjectA to know about SubClasses3 and 4 what is the proper way to register types in the class libary?

Should I pass the IoC container from ProjectA to B or use two IoC containers?

My question is not how to make library DI agnostic/non-di usable. My question is what is best practices when registering child dependencies in a class library.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
doorman
  • 15,707
  • 22
  • 80
  • 145
  • What IoC container are you using? – JuanR Feb 14 '18 at 16:12
  • But if you pass unity container from ProjectA to ProjectB then automatically know about SubClass3 and SubClass4... – Johnny Feb 14 '18 at 16:13
  • Hi @JuanR, AutoFac, but I did abstract it to my own librarí since I don´t want to depend on the vendor – doorman Feb 14 '18 at 16:13
  • Hi @Lepijohnny, yes I could pass the container from ProjectA to ProjectB. But since ProjectB doesn´t have an entry point, should I create a special method for passing the container in and registering subclass 3 and 4? – doorman Feb 14 '18 at 16:15

2 Answers2

1

My question is what is best practices when registering child dependencies in a class library.

I don´t want ProjectA to know about SubClasses3 and 4

See Ioc/DI - Why do I have to reference all layers/assemblies in entry application?.

If you follow the DI pattern, there will be no "child dependencies" of your class library. Instead, the dependencies will completely (except for maybe some abstraction libraries) be reduced to a 1 to 1 relationship with your composition root in the startup application.

It is ideal for your ProjectA application to depend on SubClasses 3 and 4. If SubClasses 3 and 4 depend on one another or components from other libraries, you drag extra dependencies along when you use SubClass 3 or SubClass 4.

Instead, put all of the coupling code in your composition root and no coupling code inside your application layer class libraries. This puts the application in charge of all of its dependencies.

If you have other types of class libraries, such as reusable application components, there are techniques that can be used to couple them but allow them to still be injectable. See DI-Friendly Library and DI-Friendly Framework.

NOTE: Putting all of the coupling code in your composition root doesn't necessarily mean you have to put it all in the same method or class. You can organize it across several classes and/or use conventions as in Ian's answer to make it maintainable. But at the end of the day, the application should be in charge of its dependencies, so you shouldn't move this code into other application layers - keep it in the startup layer of the application.

Community
  • 1
  • 1
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • Thanks @NightOwl888 ... but still it seems a bit dirty to me that ProjectA needs to know the implementation of ProjectB . I don't want ProjectA to know about the underlying mechanism of ProjectB. I think this could be achieved simply by passing in the container to ProjectB as a parameter and regeistering SubClass 3 and 4 in there. Furthermore for the ProjectB unit tests where ProjectA doesn't exists how would you register the classes, within the unit test itself? – doorman Feb 14 '18 at 17:03
  • What type of library is ProjectB? Is it an application layer, plugin, component library, or framework? If one of the latter 2, see the above linked posts. It is possible to create a fluent builder to allow for substitution of components while still making it possible to wire it together in one call. – NightOwl888 Feb 14 '18 at 17:07
  • It's just a class library containing business logic used by a webservice – doorman Feb 14 '18 at 17:09
1

Project B shouldn't need to know anything about your IoC container. If you take that approach soon all your DLLs depend on some specific IoC library.

In Autofac you can do something like this:

        builder.RegisterAssemblyTypes(typeof(ClassB).Assembly)
            .Where(x => typeof (ISomeInterface).IsAssignableFrom(x))
            .AsImplementedInterfaces();

Here I'm registering everything that implements a specific interface from another DLL. I have NO direct dependencies on any classes in that assembly and they are declared as internal to enforce that. For unit testing my test project uses [InternalsVisibleTo] in order to instantiate the classes directly but without exposing them as public.

Ian Mercer
  • 38,490
  • 8
  • 97
  • 133
  • Hi, how does ISomeInterface know what implementation to use? – doorman Feb 14 '18 at 17:17
  • I see it picks the only implemented interface? – doorman Feb 14 '18 at 17:17
  • 1
    @doorman `AsImplementedInterfaces` registers those classes as *all* of their implemented interfaces. e.g. IBackgroundService might inherit from ISomeInterface and IEmailService too. Now my app can ask for an IEmailService and get one but I don't need to register all of the separate interfaces. You can register them separately if you wish. – Ian Mercer Feb 14 '18 at 17:34
  • 2
    If Assembly B needs to do more complex registration work using the `ContainerBuilder` you can use an Autofac `Module` to at least encapsulate all of that registration in one place and to simplify the initialization code back in the main assembly. – Ian Mercer Feb 14 '18 at 17:36