.NET Shorts – Startup.cs hell

Problem

TLDR: DependencyInjection.cs

TLDR: Startup.cs

This happened a few times. You know, you are super excited about your new job and you can’t wait to see code and learn something new.

Let’s face it. They killed you on tech interview. There was a talk about microservice, patterns, advanced algorithms, messaging system and what-not. This has to be a good project, right?

Then you clone your first repository at a new job. It’s great! You can see that there is a decent architecture, everything is separated correctly, or, at least, it looks like that.

Then you open a Startup.cs file. It’s a bit too long? 500? 1000? It has a bit of database configuration setup, a bit of http clients, a couple of services/repositories, a bit of logging setup, a few swaggers here and there, some kind of questionable hardcoded authorization, error handling, logging setup, etc.

Then you move to .csproj file to check out dependencies for that project. It hurts, right? Every dependency is there and it’s getting hard to keep track of everything, especially for someone new.

Solution

I was always wondering why are we going to such lengths to create some kind of a project structure and then place everything in Startup.cs. I know, I know, it’s a container, but there HAS TO BE a better way to keep everything clean, or at least, make that public class UserRepository internal…

So, let’s assume that we have a decent project structure:

So, how can we solve this? Extensions methods!

But first, let’s take a look at one of the infrastructure layers folder structure:

And finally, let’s look at this DependencyInjection.cs class

namespace Company.Cars.Infrastructure.Database.Mssql
{
    using Company.Cars.Application.Contracts.Database;
    using Company.Cars.Infrastructure.Database.Mssql.Internal;
    using Company.Cars.Infrastructure.Database.Mssql.Internal.Repositories;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.DependencyInjection;

    public static class DependencyInjection
    {
        public static IServiceCollection 
            AddInfrastructureDatabaseMssql(
                this IServiceCollection services,
                MssqlSettings settings)
        {
            services.AddDbContext<MssqlDbContext>(
                options =>   
                    options.UseSqlServer(settings.ConnectionString));
            services.AddScoped<ICarRepository, CarRepository>();
            services.AddScoped<IUnitOfWork, UnitOfWork>();

            return services;
        }
    }

    public class MssqlSettings
    {
        public const string Key = nameof(MssqlSettings);

        public string ConnectionString { get; set; } = default!;
    }
}

Benefits?

  • You can mark everything except DepenencyInjection.cs as internal, it won’t be visible outside of this project – cleanerAutocomplete++
  • You can place all dependencies related to that project here. It will keep .csproj file nice and clean.
  • For me, this makes more sense.

Now, we can easily call this AddInfrastructureDatabaseMssql method from our Startup.cs.

  • Yes, I could have named it better…
  • And yes, sometimes I like to keep stuff in the same file…