DEV Community

loading...

Better way of storing Per-Request data across middlewares in ASP.NET Core

Thanapon Jindapitak
・3 min read

Hi developers who loves to work from home like me, for those who doesn't enjoy working from home, please skip this post. warning!

As you can guess from title, this post is about "How to not use HttpContext.Items"

To be honest, I'm the one who is still using HttpContext.Items because why not, right?--It's static! easy to write.

But no, collectively, it gives me tremendous amount of issues.

First, it's static! forget about mocking it.
Second, it's static! forget about testing it.
Third, it allows me to modify any variables I want!, brilliant !!, why is that a bad thing? because I can't control them. I can perform any add/delete/modify operations to any objects.

aight, anyways, I will show you how to use HttpContext.Items first, I know it's boring, just bear with me once. coz I don't have many things to make my blog longer. haha.. oops.

aight, let's go
HttpContext.Current.Items is a Key-Value-Pair, key and value both are type object.
You can access anywhere in your middlewares.

to store

HttpContext.Current.Items["myobject"] = new MyObject();

to read

var myObject = (IMyObject)HttpContext.Current.Items["myobject"];

to modify

var myObject = (IMyObject)HttpContext.Current.Items["myobject"];
myObject.Hime = "Hime Hime~";

easy peasy!

now let's have a look into this code one bit

app.Use(next => async context =>
{
    context.Items["DogSays"] = "Henlo hooman";
    await next(context);

    sexyMailer.Post((string)context.Items["DogSays"]);
});

app.Use(next => async context =>
{
    context.Items["DogSays"] = "Kwakkkkk!";
    await next(context);
});

now you can imagine if you work with 1500+ developers, how troublesome to maintain your calm, when you're assigned to find who changes our dog to says "Kwakkkkk!".

enough playing with time.

I found 2 elegant ways

[1.] use IHttpContextAccessor this interface is to read HttpContext through DI, simply you can register it by
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// this has to be registerd as singleton as per this announcement ref.
or in ASP.NET Core 2.1 or above, you can use extension method services.AddHttpContextAccessor();

now in your class, you can access context via DI
for example,

public class HomeService{
    private readonly IHttpContextAccessor _httpContextAccessor;
    public HomeSerivce(IHttpContextAccessor httpContextAccessor){
        _httpContextAccessor = httpContextAccessor;
    }
    public DoWork(){
        var says = (string)_httpContextAccessor.HttpContext.Items["DogSays"]
        Log.Rock(says);
    }
}

now you can mock your IHttpContextAccessor quite easily! and live a happy life.

[2.] Create a Class to handle the modification of this shared objects, and register it as Scoped (created once per client request (connection) ref.) then you can use it through DI, also it's a class! you can do a typed properties

so do speak, here is the code.

public interface IVaultAccessor<T> {
    void AddToVault(T t);
    T GetFromVault(T t);
}

public class VaultAccessor<MyDog> : IVaultAccessor<MyDog> {
    private MyDog _myDog { get; set; }

    public void AddToVault(MyDog myDog) {
        _myDog = myDog;
    }

    public MyDog GetFromVault() {
        return _myDog;
    }
}

public class MyDog {
    public string WhatDoesTheDogSay { get; set; }
}

// register as Scoped
services.AddScoped<IVaultAccessor<MyDog>>(sp => new VaultAccessor<MyDog>());


// Usage in any random class
public class Tea: ITea {
    private readonly IVaultAccessor<MyDog> _myDogAccessor;
    public Tea(IVaultAccessor<MyDog> myDogAccessor) {
        _myDogAccessor = myDogAccessor;
    }

    public void RandomName() {
        var myDog = _myDogAccessor.GetFromVault();
        Log.Rock($"What a relief you are still my dog, {myDog.WhatDoesTheDogSay}");
    }
}

You can make IVaultAccessor fancy to the extent. And live a happy life.

Whoaaa! that's it for today.
How was it guys, hope you enjoy being chubbier.

See you around.

Discussion (0)