Dependency Injection on an N-Tier solution - Remove Boilerplate

87 Views Asked by At

I am creating a solution that has an API (Microsoft.NET.Sdk.Web), domain (Microsoft.NET.Sdk), and MVC projects (Microsoft.NET.Sdk.Web) targeting .NET 8.

In my domain I have two classes that look like this:

public class ApplicationUserQuery
{

    private readonly string _connectionString;

    public ApplicationUserQuery(string connectionString)
    {
        _connectionString = connectionString;
    }

    // ...
}

public class ApplicationUserService
{

    private readonly ApplicationUserQuery _applicationUserQuery;

    public ApplicationUserService(ApplicationUserQuery applicationUserQuery)
    {
        _applicationUserQuery = applicationUserQuery;
    }

    // ...
}

To use the classes in my API project I am doing the following in my Program.cs:

var applicationDatabaseConnectionString = builder.Configuration.GetConnectionString("ApplicationDatabase");
if (applicationDatabaseConnectionString == null)
{
    throw new ArgumentNullException(nameof(applicationDatabaseConnectionString));
}
builder.Services.AddScoped(_ => new ApplicationUserQuery(applicationDatabaseConnectionString));
builder.Services.AddScoped<ApplicationUserService>();

This works, but it seems like a lot of boilerplate just for two classes and will continue to grow for every service/query class I create in my domain. Is there anyway to simplify this using the built-in .NET core stuff? I'm wanting to avoid using a 3rd party library like Ninject because of issues I have had with Ninject in particular on projects I do at work.

2

There are 2 best solutions below

9
Zdeněk Jelínek On BEST ANSWER

The .NET Extensions DI is intended to be used like the example you provided. It is a rudimentary tool that you can build upon if you like, it has very little opinions. Features like named (keyed) services was just introduced in .NET 8, years after initial release.

If you want to auto-register the dependencies, use a different library or implement it yourself - e.g. take the domain Assembly, enumerate all types that end with "Query" and register them as scoped, it's not that much code and you have it under your control.

2
Train On

Why not just inject IConfiguration like this

public class ApplicationUserQuery
{
    private readonly string _connectionString;

    public ApplicationUserQuery(IConfiguration config)
    {
        _connectionString = config["ApplicationDatabase"];
        // do you really need this checking?
        // just make an extension method in another file called something like HostExtensions.cs and call them in the program.cs for any null config valules
        // checks if you really need to.
        if(string.isNullOrWhiteSpace(_connectionString) {//throw exception}
    }
}

Then in Program.cs

builder.Services.AddScoped<ApplicationUserQuery>();
builder.Services.AddScoped<ApplicationUserService>();

Judging just by what you posted (I don't have an overview of what your architecture looks like) it looks like you have a service layer then a repo layer. For what? Why do need those separation of concerns? I could be wrong (Because I don't know what you're building) but it looks like you're over architecting a solution for no reason. Hence all the boiler plate code you're writing.