How to use .net core dependency injection in multiprojects solution?
Asked Answered
B

1

5

I'm new to asp.net core.
What I'm trying to do is to build multi projects solution and use dependency injection to pass interfaces between projects.
What I know is that in ASP.NET core project we have ConfigureServices method in startup.cs file to register our interfaces and their implementations like this:

 public void ConfigureServices(IServiceCollection services)
 {
   // Add framework services.
   services.AddMvc();
   services.AddTransient<IMyInterface,MyImplementation>();
   .....
 }

This is good if you have classes all in the same project, but what if I have multi projects ?
Usually what I'll do is create separate project with installers (Windsor installers) to register the needed interfaces and their implementations.

In .net core we can do this by creating static ServiceCollection(); and get from it static IServiceProvider to use it any time to get any service you register:

public static IServiceCollection _serviceCollection { get; private set; }
public static IServiceProvider serviceProvider { get; private set; }
public static RegisterationMethod() {
   _serviceCollection = new ServiceCollection();

   _serviceCollection.AddSingleton<IMyInterface,MyImplementation>();
   .....
   serviceProvider = _serviceCollection.BuildServiceProvider();
}

public T GetService<T>() where T : class
{
   return serviceProvider.GetService<T>();
}

Now we call RegisterationMethod from ower startup project and continue to develop as usual with always registering the services in this class.
The problem in this approach is if I used it in ASP.NET core project I'll have two places to register the services, this one and the one in the startup.cs file which has ConfigureServices(IServiceCollection services) .
You may say,

OK pass IServiceCollection you had in ConfigureServices(IServiceCollection services) to the RegisterationMethod you previously created, in this way you're using the same services collection that ASP.NET using.

But in this way I'll be tight coupled to the dependency injection module of the .net core.

Is there more clean way to do this ? or should I have replace the default DI with Windsor for example ?

Borrego answered 22/10, 2016 at 19:55 Comment(0)
J
8

...in ASP.NET core project[s] we have ConfigureServices... to register our interfaces and their implementations... This is good if you have classes all in the same project, but what if I have multi projects?

It doesn't matter that you have multi projects. The same principle applies:

Put your composition root in your application, as close to the entry point as possible.

Lets assume that you have a single application that references several class libraries. In your application's Startup class, use ConfigureServices to register all of the dependencies. In each of the class library projects, use constructor injection. It does not matter whether your classes live in the same or in different projects.

OK pass IServiceCollection you had in ConfigureServices(IServiceCollection services) to the RegisterationMethod you previously created, in this way you're using the same services collection that ASP.NET using.

Yes, that's the way to do it. Here is an example from the github.com/aspnet/logging repository:

public static IServiceCollection AddLogging(this IServiceCollection services)
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }

    services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
    services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));

    return services;
}

Based on your comments...

...it sounds like you are trying to avoid having a composition root in your application. The composition root is the single location where we register dependencies with the dependency injection container. The composition root is placed as close as possible to the application's entry point (the ConfigureServices method, for instance) and it belongs in the application not in its libraries.

Jarrett answered 22/10, 2016 at 20:10 Comment(11)
I have asp.net core project, and multi layers: (services , repositories , ..), the way you suggest is to reference all my interfaces and implementations in this web project, I don't this it's a good way to do it right ? that's why I want to create separate project to register all of them.Borrego
@dabbas Let me see if I am understanding correctly. You have a single application (MyApp). You also have a bunch of class libraries, which include services and repositories (MyService, MyRepository, et cetera). Is that right?Jarrett
Yes correct, so that MyApp will actually only have reference to MyServices to get\set data and MyServices only reference MyRepositories ...Borrego
@Borrego Based on that, it seems correct to register all of the dependencies inside ConfigureServices and to reference all of your interfaces and implementations in the web project. Why do you not think this is right?Jarrett
I don't think this is a good practice, this way you have access to MyRepositories implementations, so why to you need repository layer ? previously I used to have core project that register all layers and call the registration method in it from my start project only.Borrego
@dabbas My sense is that, inside the Startup class, yes, you have a reference to MyRepositories, but that's only to register it as the implementation of IMyRepository. Through the rest of the application and its class libraries, you will program against interfaces. The Startup class is where you register the services that implement those interfaces. This seems reasonable to me. The repository layer, in this case, will hide the data layer's implementation from the services.Jarrett
Sorry but this is not what I'm looking for, I need a way to get the services collection in another project without creating new one.Borrego
Let us continue this discussion in chat.Jarrett
@Dabbas: The application is the place where you wire up the DI, because from application to application it may vary how you use it so its impossible to let the libraries handle/bootstrap their own. ASP.NET Core libraries usually abstract this behind a AddXxx() method and .UseXxx (middleware registration), so you can just do services.AddMyAppBusinessServices() etc. in your Startup class. There is no out of the box module support like Unity or Windsor, you have to write extension methods or your own module system or use 3rd party IoC containerBarraza
@Barraza I thought I could do this in some way that I get the instance of `IServiceCollection' and register my services with it, without referencing all the interfaces and implementations to the main app, looks like you suggestion is the closest to what I wanted, please add it as an answer to choose it.Borrego
@Dabbas: Nothing prevents you from doing so, create a class, pass IServiceCollection to it during startup and register it here. But that's no different from creating a static class in your library project with a signature of public static IServiceCollection AddMyBusinessServices(this IServiceCollection services) and register your stuff inside, then put services.AddMyBusinessServices() in your Startup.cs. You need the reference anyways. If you don't want the classes to be instantiated in the application project, mark them or their constructor as internalBarraza

© 2022 - 2024 — McMap. All rights reserved.