'Where to store license information that I will need at multiple levels?

I'm developing .Net 6 API.

My project includes controllers, services and repositories (using dependency injection).

I also added a permission check via middleware:

Program.cs

app.UseMiddleware<AuthMiddleware>();

AuthMiddleware.cs

public class AuthMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<EcmAuthMiddleware> _logger;
    private readonly IConfiguration _config;

    public AuthMiddleware(RequestDelegate next, 
      ILogger<AuthMiddleware> logger, IConfiguration config)
    {
       _next = next;
       _logger = logger;
       _config = config;
    }

    public async Task Invoke(HttpContext context, IUserApiService 
     userApiService)
    {
        ...
        context.Items["Instance"] = instance;
        await _next(context);
    }
}

From here I get the customer (and database) to run the APIs on.

Now I need to get some license information (via external API) from the newly obtained client and store it somewhere.

I tried invoking the call from the controller but would have to repeat it for almost all controllers. So I thought about transferring the call to middleware.

From the call I will have various information that I would like to store for use by the underlying levels: controllers, services and repositories. I'd rather not use the session or coookie.

Can I use only httpcontext or are there other solutions?

context.Items["LicenseInfo"] = licenseInfo;

This information is valid only for the call of an api then it should not be stored (eg Application).

EDIT: GetLicenseInfo() must contains an external call:

string result = await _userApiService.GetUserApiResponseAsString("users/token", HttpMethod.Get, applicationId, token);


Solution 1:[1]

Can I use only httpcontext or are there other solutions?

There's nothing wrong with using HttpContext.Items for this. It's exactly what HttpContext.Items is for: attaching contextual data to an HTTP request. With this kind of "dictionary of objects" API, I do like to wrap my own APIs around it for type safety and simplicity:

public static class HttpContextLicenseInfoExtensions
{
  public static void SetLicenceInfo(this HttpContext context, LicenseInfo licenseInfo) =>
      context.Items[key] = licenseInfo;
  public static LicenseInfo? TryGetLicenseInfo(this HttpContext context) =>
      context.Items[key] as LicenseInfo;
  public static LicenseInfo GetLicenseInfo(this HttpContext context) =>
      context.TryGetLicenseInfo() ?? throw new InvalidOperationException("No license info.");

  private static readonly string key = Guid.NewGuid().ToString("N");
}

// Example middleware
app.Use(async (context, next) =>
{
  context.SetLicenseInfo(licenseInfo);
  await next.Invoke();
});

// Example usage
var licenseInfo = HttpContext.GetLicenseInfo();

But if you really want to avoid HttpContext.Items, you can use AsyncLocal<T>. You just want to structure the API so that you set the value for a specific scope (I like to return IDisposable to un-set the value), and then you usually inject an "accessor" to read the current value. Something like this should work (using Disposable from my disposables library):

public static class AsyncLocalLicenseInfo
{
  public static IDisposable Set(LicenseInfo licenseInfo)
  {
    var originalValue = local.Value;
    local.Value = licenseInfo;
    return new Disposable(() => local.Value = originalValue);
  }

  public static LicenseInfo? TryGet() => local.Value;

  public static LicenseInfo LicenseInfo => TryGet() ?? throw new InvalidOperationException("No license info.");

  private static readonly AsyncLocal<LicenseInfo> local = new();
}

// Example middleware
app.Use(async (context, next) =>
{
  using var localValue = AsyncLocalLicenseInfo.Set(licenseInfo);
  await next.Invoke();
});

// Example usage
var licenseInfo = AsyncLocalLicenseInfo.LicenseInfo;

If you don't like the static API, you can hide it behind an "accessor":

// Inject this into downstream types
public interface ILicenseInfoAccessor
{
  LicenseInfo LicenseInfo { get; }
}

public sealed class LicenseInfoAccessor : ILicenseInfoAccessor
{
  public LicenseInfo LicenseInfo => AsyncLocalLicenseInfo.LicenseInfo;
}

// Example usage
var licenseInfo = licenseInfoAccessor.LicenseInfo;

Solution 2:[2]

This is only an alternative to MiddleWare which might be a better option, might not, it depends on a lot of things (as in most software dev questions).

This could be the factory:

public class LicenseInfoFactory
{
    public LicenseInfoFactory(IHttpContextAccessor context)
    {
      _context = context;
    }

    private readonly IHttpContextAccessor _context;

    public LicenseInfo Build()
    {
        _context...
        // api call to get/build/return LicenseInfo
    }
}

And then in your startup:

services.AddHttpContextAccessor();
services.AddSingleton<LicenseInfoFactory>();
services.AddScoped<LicenseInfo>(provider => { 
   var factory = provider.GetRequiredService<LicenseInfoFactory>();
   return factory.Build();
});

Then just inject LicenseInfo in your controllers/repositories constructors etc:

public MyController(LicenseInfo licenseInfo)

This should only make one api call per scoped request. (this is from memory, syntax might not be exact)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Stephen Cleary
Solution 2