Programming

Easy methods to resolve dependencies in .NET APIs primarily based on present HTTP Request


Say that you’ve got an interface and that you just wish to specify its concrete class at runtime utilizing the native Dependency Injection engine offered by .NET.

As an example, think about that you’ve got a .NET API undertaking and that the flag that tells the applying which dependency use is ready within the HTTP Request.

Can we do it? In fact, sure – in any other case I would not be right here writing this text 😅 Let’s learn the way!

Why use completely different dependencies?

However first: does all of this make sense? Is there any case once you wish to inject completely different companies at runtime?

Let me share with you a narrative: as soon as I needed to create an API undertaking which uncovered only a single endpoint: Course of(string ID).

That endpoint learn the merchandise with that ID from a DB – an object composed of some information and a few a whole lot of youngsters IDs – after which referred to as an exterior service to obtain an XML file for each youngster ID within the object; then, each downloaded XML file can be saved on the file system of the server the place the API was deployed to. Lastly, a TXT file with the listing of the objects appropriately saved on the file system was generated.

Fairly a simple activity: learn from DB, name some APIs, retailer the file, retailer the report file. Nothing extra.

However, how one can run it domestically with out saving a whole lot of recordsdata for each HTTP name?

I made a decision so as to add a easy Question Parameter to the HTTP path and let .NET perceive whether or not use the concrete class or a pretend one. Let’s examine how.

Outline the companies on ConfigureServices

As you could know, the dependencies are outlined within the ConfigureServices technique contained in the Startup class.

Right here we will outline our dependencies. For this instance, we’ve an interface, IFileSystemAccess, which is applied by two lessons: FakeFileSystemAccess and RealFileSystemAccess.

So, to outline these mutable dependencies, you possibly can comply with this snippet:

public void ConfigureServices(IServiceCollection companies)
{
    companies.AddControllers();
    
    companies.AddHttpContextAccessor();

    companies.AddTransient<FakeFileSystemAccess>();
    companies.AddTransient<RealFileSystemAccess>();

    companies.AddScoped<IFileSystemAccess>(supplier =>
    {
        var context = supplier.GetRequiredService<IHttpContextAccessor>();

        var useFakeFileSystemAccess = context.HttpContext?.Request?.Question?.ContainsKey("fake-fs") ?? false;

        if (useFakeFileSystemAccess)
            return supplier.GetRequiredService<FakeFileSystemAccess>();
        else
            return supplier.GetRequiredService<RealFileSystemAccess>();
    });
}

As regular, let’s break it down:

Inject dependencies utilizing a Manufacturing unit

Let’s start with the king of the article:

companies.AddScoped<IFileSystemAccess>(supplier =>
{
}

We are able to outline our dependencies by utilizing a manufacturing facility. As an example, now we’re utilizing the AddScoped Extension Technique (wanna know some attention-grabbing details about Extension Strategies?):




















public static IServiceCollection AddScoped<TService>(this IServiceCollection companies, Func<IServiceProvider, TService> implementationFactory) the place TService : class;

This Extension Technique permits us to get the details about the companies already injected within the present IServiceCollection occasion and use them to outline how one can instantiate the precise dependency for the TService – in our case, IFileSystemAccess.

Why is that this a Scoped dependency? As you may bear in mind from a earlier article, in .NET we’ve 3 lifetimes for dependencies: Singleton, Scoped, and Transient. Scoped dependencies are those that get loaded as soon as per HTTP request: due to this fact, these are your best option for this particular instance.

Studying from Question String

Since we have to learn a worth from the question string, we have to entry the HttpRequest object.

That is why we’ve:

var context = supplier.GetRequiredService<IHttpContextAccessor>();
var useFakeFileSystemAccess = context.HttpContext?.Request?.Question?.ContainsKey("fake-fs") ?? false;

Right here I am getting the HTTP Context and checking if the fake-fs secret is outlined. Sure, I do know, I am not checking its precise worth: I am simply checking whether or not the important thing exists or not.

IHttpContextAccessor is the important thing a part of this snippet: it is a service that acts as a wrap across the HttpContext object. You may inject it in every single place in your code, however at one situation: it’s a must to outline it within the ConfigureServices technique.

How? Properly, that is easy:

companies.AddHttpContextAccessor();

Injecting the dependencies primarily based on the request

Lastly, we will outline which dependency have to be injected for the present HTTP Request:

if (useFakeFileSystemAccess)
    return supplier.GetRequiredService<FakeFileSystemAccess>();
else
    return supplier.GetRequiredService<RealFileSystemAccess>();

Do not forget that we’re inside a manufacturing facility technique: which means, relying on the worth of useFakeFileSystemAccess, we’re defining the concrete class of IFileSystemAccess.

GetRequiredService<T> returns the occasion of sort T injected within the DI engine. This suggests that we’ve to inject the 2 completely different companies earlier than accessing them. That is why you see:

companies.AddTransient<FakeFileSystemAccess>();
companies.AddTransient<RealFileSystemAccess>();

These two traces of code serve two completely different functions:

  1. they make these companies out there to the GetRequiredService technique;
  2. they resolve all of the dependencies injected in these companies

Working the instance

Now that we’ve every thing in place, it is time to put it into follow.

Initially, we want a Controller with the endpoint we’ll name:

[ApiController]
[Route("[controller]")]
public class StorageController : ControllerBase
{
    non-public readonly IFileSystemAccess _fileSystemAccess;

    public StorageController(IFileSystemAccess fileSystemAccess)
    {
        _fileSystemAccess = fileSystemAccess;
    }

    [HttpPost]
    public async Job<IActionResult> SaveContent([FromBody] FileInfo content material)
    {
        string filename = $"file-{Guid.NewGuid()}.txt";
        var saveResult = await _fileSystemAccess.WriteOnFile(filename, content material.Content material);
        return Okay(saveResult);
    }

    public class FileInfo
    {
        public string Content material { get; set; }
    }
}

Nothing fancy: this POST endpoint receives an object with some textual content, and calls IFileSystemAccess to retailer the file. Then, it returns the results of the operation.

Then, we’ve the interface:

public interface IFileSystemAccess
{
    Job<FileSystemSaveResult> WriteOnFile(string fileName, string content material);
}

public class FileSystemSaveResult
{
    public FileSystemSaveResult(string message)
    {
        Message = message;
    }

    public string Message { get; set; }
}

which is applied by the 2 lessons:

public class FakeFileSystemAccess : IFileSystemAccess
{
    public Job<FileSystemSaveResult> WriteOnFile(string fileName, string content material)
    {
        return Job.FromResult(new FileSystemSaveResult("Used mock File System entry"));
    }
}

and

public class RealFileSystemAccess : IFileSystemAccess
{
    public async Job<FileSystemSaveResult> WriteOnFile(string fileName, string content material)
    {
        await File.WriteAllTextAsync(fileName, content material);
        return new FileSystemSaveResult("Used actual File System entry");
    }
}

As you can have imagined, solely RealFileSystemAccess really writes on the file system. However each of them return an object with a message that tells us which class accomplished the operation.

Let’s examine it in follow:

Initially, let’s name the endpoint with out something in Question String:

Without specifying the flag in Query String, we are using the real file system access

And, then, let’s add the important thing:

By adding the flag, we are using the mock class, so that we don't create real files

As anticipated, relying on the question string, we will see two completely different outcomes.

In fact, you should utilize this technique not solely with values from the Question String, but in addition from HTTP Headers, cookies, and no matter comes with the HTTP Request.

Additional readings

If you happen to bear in mind, we have outlined the dependency to IFileSystemAccess as Scoped. Why? What are the opposite lifetimes native on .NET?

🔗 Dependency Injection lifetimes in .NET | Code4IT

Additionally, AddScoped is the Extension Technique that we used to construct our dependencies due to a Manufacturing unit. This is an article about some superior subjects about Extension Strategies:

🔗 How one can create Extension Strategies in C# | Code4IT

Lastly, the repository for the code used for this text:

🔗 DependencyInjectionByHttpRequest undertaking | GitHub

Wrapping up

On this article, we have seen that we will use a Manufacturing unit to outline at runtime which class will likely be used when resolving a Dependency.

We have used a easy calculation primarily based on the present HTTP request, however in fact, there are a lot of different methods to realize the same outcome.

What would you employ as an alternative? Have you ever ever used the same method? And why?

Pleased coding!

🐧

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button