0

Have a project structure where I have a couple of layers

Api
Bll
Dal
Utility

When say a order request is received by the Api there is a couple of steps that we need to take. Like:

  • Validate the input
  • Save the customer
  • Save the order
  • Validate payment
  • Save final order status

All of these require different classes from the Bll And the classes inside the Bll requires classes from Dal and maybe other Bll or from Utility.

So now inside the Api I need to register the whole chain of what might be needed like

Register<IValidateService,ValidateService>()
Register<ICustomerService,CustomerService>()
Register<ICustomerDatabaseService,CustomerDatabaseService>()
Register<IUtilityService,UtilityService>();

Maybe all of the above just to get the CustomerService working, and then we need to do this for a lot more services and I will have to reference the Dal layer inside the Api layer. And in the long run I feel that everything will become really bloated. Would be good if I could just Register the CustomerService and then that will register it's dependencies by itself or something.

Maybe it is fine to have the entry level to be aware of everything?

Any ideas on how to solve this or am I overthinking things?

Thank you

  • 3
    you'll find a good answer [here](https://stackoverflow.com/q/9501604/1515209) – qujck May 20 '22 at 14:50
  • I think the concept you are looking for is `Auto-Registration`. This is a feature that is not supported OOTB by MS.DI, but you can use [Scrutor](https://github.com/khellang/Scrutor) for instance to help you out. – Steven May 20 '22 at 15:05
  • I feel that auto-registration is just a way of hiding the problem. And in the other example I guess that the Api act as a composition root, and yes it makes it easy to test and more lose coupled but I still don't like the idea of having a file in the Api where I reference every single service in all layers that will be used sooner or later. – Andreas Andersson May 20 '22 at 20:02

2 Answers2

0

My suggested solution for auto-registration is the following:

  • Use Autofac.
  • Create a public DependencyModule class derived from Autofac.Module in your Api, Bll, Dal and Utility projects.
  • Override the Load method and register only types that are in that project.
  • In your startup project (Api) use my nuget package to automatically discover and register all your DependencyModule classes into the DI container.

At the end you will have something like this:

  • Utility
    • DependencyModule.cs - registers all the utility types that need to be injected.
  • Dal
    • DependencyModule.cs - registers all the DAL types (e.g. DbContext) that need to be injected.
  • Bll
    • DependencyModule.cs - registers all the BLL types that need to be injected.
  • Api
    • DependencyModule.cs - registers all the API types (if any) that need to be injected. E.g. filters, etc.
    • In Program.cs or Startup.cs you register only my Autofac module that will discover and register all your modules above.

See my example solution's description and implementation.

This way each injectable type registration is done in its own assembly and dependent services do not need to worry about it.

Alternative solution - uses Microsoft DI

Instead of Autofac modules you can create extensions methods for IServiceCollection type in each of your project and register the types that are in that project.

Then in your Program.cs or Startup.cs just call each extensions method.

At the end you will have something like this:

  • Utility
    • IServiceCollectionExtensions.cs - registers all the utility types that need to be injected.
  • Dal
    • IServiceCollectionExtensions.cs - registers all the DAL types (e.g. DbContext) that need to be injected.
  • Bll
    • IServiceCollectionExtensions.cs - registers all the BLL types that need to be injected.
  • Api
    • IServiceCollectionExtensions.cs - registers all the API types (if any) that need to be injected. E.g. filters, etc.
    • In Program.cs or Startup.cs call each of the extensions methods.

Note

Actually you can combine MS DI with Autofac so that you can enjoy the advanced features of Autofac and use specific extension methods for IServiceCollection at the same time.

In that case you should know that the order of registrations is this:

  1. MS DI registrations: ConfigureServices() method
  2. Autofac registrations: ConfigureContainer<T>() method

All the MS DI registrations will be populated into the Autofac container.

Gabor
  • 3,021
  • 1
  • 11
  • 20
0

Dependency injection should be done at the application layer, which means the application must specify (effectively, choose) all of the dependencies in order for it to work correctly. This does mean it will be "bloated" in the wiring/startup phase, and does mean the app layer will have to deal with dependencies it might not normally care about.

That said, there's nothing wrong with your library code providing sane base implementations and wiring of these to alleviate the app layer's burden of figuring out what to wire up.

That means you can do one of

  • add the statements for registration manually and explicitly in your startup (total control, and extremely obvious how things are setup)
  • create a convenience method that contains common wiring in your library (less control, but less code to deal with when wiring). This is common in ASP.NET (see the AddXxx() and UseXxx() patterns).
  • discover dependencies. This uses reflection (usually) to find the implementations of all the dependent interfaces, and auto-register them). This is usually from a third-party like AutoFac. It's not built in to .NET.
Kit
  • 20,354
  • 4
  • 60
  • 103