Search code examples
c#dependency-injectionsimple-injector

Ambient data as AsyncLocal


I have created an AmbientDataProvider that is intended to be to access query params that are set in a lot of my requests. In a few cases, I would like to set it manually, e.g.

[HttpGet]
public IHttpActionResult SomeAction()
{
    _ambientDataProvider.SetRequestId("123");

    return Ok();
} 

Is there any point of keeping it as AsyncLocal below? Since the class has a scoped lifestyle, I guess I might as well use a private string?

public class AmbientDataProvider : IAmbientDataProvider
{
    private readonly AsyncLocal<string> _requestId = new AsyncLocal<string>();
    public string RequestId => _requestId.Value ?? HttpContext.Current?.Request.QueryString["requestId"];

    public void SetRequestId(string requestId)
    {
        _requestId.Value = requestId;
    }
}

My container configuration

container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
container.Register<IAmbientDataProvider, AmbientDataProvider>(Lifestyle.Scoped);

Solution

  • When it comes to pushing runtime data through an object graph, there are two DI Composition Models to choose from: Ambient Composition Model and Closure Composition Model.

    You seem to mixing the two models. With the Closure Composition Model you store runtime data (your request id) in a (private) field inside a component, whereas with the Ambient Composition Model, you store that runtime data as ambient state (for instance using AsyncLocal<T>).

    When using the Ambient Composition Model, you can make your AmbientDataProvider singleton, because the AsyncLocal<T> already ensures isolation of runtime data per request. When your data provider is a Scoped component, however, you can also store the data in a private field.