Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I split up my service registations into different files in ASP.NET Core?

If you've ever used Windsor Installers then you know what I'm talking about. You can have different installers for different parts of the application (repositories, logic, etc.)

public class RepositoriesInstaller : IWindsorInstaller
{
   public void Install(IWindsorContainer container, IConfigurationStore store)
   {
      container.Register(...);
   }
}

And then we could call the installer from the Windsor bootstrapper like so

var container = new WindsorContainer();
container.Install(new RepositoriesInstaller());

Does asp.net core have anything similar?

If not I would implement it like so

public interface IServiceConfiguration
{
    void Install(IServiceCollection services);
}

public class DomainServiceConfiguration : IServiceConfiguration
{
    public void Install(IServiceCollection services)
    {
        services.AddScoped(...);
    }
}

And then call this from the startup file

new DomainServiceConfiguration().Install(services);
like image 540
Storm Muller Avatar asked Feb 13 '18 17:02

Storm Muller


1 Answers

So as many people mentioned in the comments, the IOC container in asp.net core is simple and light weight. However it is extensible.

The convention would be to write an extension method in a different file and call in the ConfigureServices method (So core's DI has no such thing as a module installer interface). This answers the question I posted originally.

My argument was that you would not be able to register the methods by convention. I.e. create the extension method and not have to worry about adding it to the ConfigureServices class.

I was wrong, this can be done. Here's the code:

This class lives in any .cs file within your assembly

public static class MyInstaller
{
    public static void Install(this IServiceCollection services)
    {
        // register stuff here
    }
}

Add This Method to your Startup.cs file

void LoadInstaller(Type type, IServiceCollection services)
{
    var installMethods= type.GetMethods(BindingFlags.Static | BindingFlags.Public).Where(mi => mi.Name == "Install");

    var installMethod = installMethods.First();

    installMethod.Invoke(null, new object[] { services });
}

Add this to your ConfigureServices method

var assemblies = AppDomain.CurrentDomain.GetAssemblies().
        Where(assembly => assembly.GetName().Name.Contains("MyAssemblyName"));

        foreach (var assembly in assemblies)
        {
            var types = assembly.GetTypes().Where(t => t.IsClass && t.IsPublic && t.Name.Contains("Installer")); // You can create your own convention here, make sure it won't conflict with other class names that are not meant to be installers

            foreach (var installerType in types)
            {
                LoadInstaller(installerType, services);
            }
        }

Now you can add installers by convention and you never have to manually call the extension method for each new installer. The code could be cleaned up by adding error handling and throwing useful exceptions. But the idea is sound.

EDIT:

Seeing that this answer seems to gaining attention, I thought I'd add Microsoft's recommendation to a similar problem

Each services.Add{SERVICE_NAME} extension method adds, and potentially configures, services. We recommended that apps follow this convention. Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations. Including the namespace portion Microsoft.Extensions.DependencyInjection for DI extension methods also:

Allows them to be displayed in IntelliSense without adding additional using blocks. Prevents excessive using statements in the Program or Startup classes where these extension methods are typically called.

like image 114
Storm Muller Avatar answered Sep 21 '22 06:09

Storm Muller