13

I have an ASP.NET Core 3.1 project. Typically, I register any dependency using the ConfigureServices() method in the Startup.cs class.

But, I find myself having to register lots of dependencies and the ConfigureServices() looks huge! I know I can probably create an extension method of a static method and call it from the ConfigureService()` class, but wondering if there is a better way.

If there a way to register dependencies in the IoC container without having to define them one at a time like this

services.AddScoped<Interface, Class>();
.... 200 lines later
services.AddScoped<ISettings, Settings>()

3 Answers3

14

Grouping related dependencies into custom extension methods is a very common way to do this. ASP.NET Core already does this for many of the internal services, and you can easily expand on top of that and set them up the way you need for your application. For example to set up authentication and authorization:

public IServiceCollection AddSecurity(this IServiceCollection services)
{
    services.AddAuthentication()
        .AddCookie();

    service.AddAuthorization(options =>
    {
        options.DefaultPolicy = …;
    });

    return services;
}

You can do the same for your application-specific services and group them logically in separate extension methods.

If you have a lot of service registrations that are very similar, you can also employ a convention-based registration e.g. using Scrutor. For example, this registers all services within a certain namespace as transient for their respective interface:

services.Scan(scan => scan
    .FromAssemblyOf<Startup>()
        .AddClasses(c => c.InNamespaces("MyApp.Services"))
            .AsImplementedInterfaces()
            .WithTransientLifetime()
);

Scrutor allows for very complex rules to scan for services, so if your services do follow some pattern, you will likely be able to come up with a rule for that.

Nkosi
  • 191,971
  • 29
  • 311
  • 378
poke
  • 307,619
  • 61
  • 472
  • 533
3

Create a custom attribute (called AutoBindAttribute)

public class AutoBindAttribute : Attribute
{
}

Use it like bellow (Decorate all the implementations that you want to automatically bind with [AutroBind])

public interface IMyClass {}

[AutoBind]
public class MyClass : IMyClass {}

Now create an extention method for IServiceCollection

public static class ServiceCollectionExtensions
{
    public static void AutoBind(this IServiceCollection source, params Assembly[] assemblies)
    {
       source.Scan(scan => scan.FromAssemblies(assemblies)
        .AddClasses(classes => classes.WithAttribute<AutoBindAttribute>())
        .AsImplementedInterfaces()
        .WithTransientLifetime();
    }
}

Now call it in Startup.cs

public class Startup
{

    public void ConfigureServices(IServiceCollection services)
    {
        services.AutoBind(typeof(Startup).Assembly);
    }

}

Note: You can improve the ServiceCollectionExtentions class to support all scopes such as singleton, etc. This example shows only for Transient lifetime.

Enjoy!!!

Dilhan Jayathilake
  • 1,561
  • 2
  • 13
  • 15
0

In addition to what was mentioned.

Personally I like to have a separate class registering dependencies per each assembly. This adds more control on using classes at the right layer and allows to make them internal which IMO is good.

Whether to use scan mechanisms or not is up to you. Some frameworks give this by default. Grouping similar dependencies in a set of classes/methods, in turn, should help to keep resolving logic in a consistent place for any changes. You can combine both approaches.

Alex Kovanev
  • 1,788
  • 1
  • 15
  • 26