RequestContext issue after upgrading to 5.10.4

I am having some problems after upgrading the ServiceStack version from version 5.9.2 to 5.10.4.
In the version 5.9.2, I could add items to RequestContext.Instance.Items during PreRequestFilters.Add method.

PreRequestFilters.Add((req, resp) => {
    var outerScope = req.Resolve<ILifetimeScope>();
    var requestScope = outerScope.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
    RequestContext.Instance.Items["AutofacScope"] = requestScope;
});

Then I could retrieve the items collection (RequestContext.Instance.Items) in GlobalRequestFilters.Add method.

GlobalRequestFilters.Add((req, resp, dto) => {
    var scope = req.Resolve<ILifetimeScope>();

    /// ... removed for brevity.
}

This behaviour seems to have changed in version 5.10.4 as I see RequestContext.Instance.Items collection is now cleared.
Can you please suggest if the current behaviour has changed in the new version, and what should be the recommended way to access RequestContext.Instance.Items in the newer versions?

1 Like

Hi @Aaron , I’m not able to reproduce the issue and not aware of changes that would cause the Items to get cleared between PreRequestFilters and GlobalRequestFilters. Can you confirm req.Items is empty before and after and share a minimal reproduction of the issue via a GitHub repository? Once I can reproduce the problem, I can help you find the root cause and look at possible solutions.

Can RequestContext.Instance.Items be lost between async method calls?

I have found some problems with call contexts/threadlocals on .Net Framework, and had to work around it.

Isolating the issues some time takes a bit, because it might depend on what is called and when, as filters go.

The behavior of the RequestContext singleton is different per platform/framework and has to use whatever construct is available to it in the host it’s running in.

In ASP .NET Core it uses AsyncLocal which should maintain its context across async hops, in ASP .NET Framework Apps it uses HttpContext.Current which should also preserve state across async continuations, but in .NET Framework / HttpListener hosts it has to use CallContext which can have issues in some async code. In this case it can also be configured to use RequestContext.UseThreadStatic=true to force it to use a ThreadStatic dictionary, which doesn’t work across async continuations, but can resolve issues with CallContext/HttpListener self host in Unit tests.

Since its behavior is dependent on the platform it’s running in, we recommend not to resolve the Request Context from a singleton context and to always use IRequest.Items and pass the IRequest instance down to whatever method needs access to per request storage.

Thanks, yes it makes sense to no longer use singletons like RequestContext.Instance. And all the places but one we can change to pass the IRequest and use that instead. The place we can’t do that is in IContainerAdapter that bridges between Funq and Autofac. Is there a way the Request can be passed through to this?

 public class AutofacContainerAdapter : IContainerAdapter
 {
     private readonly Autofac.IContainer _container;
     public AutofacContainerAdapter(Autofac.IContainer container) => _container = container;

     public T TryResolve<T>()
     {
         var scope = (ILifetimeScope)RequestContext.Instance.Items["AutofacScope"];
         if (scope != null && scope.TryResolve(typeof(T), out var scopeComponent)) {
             return (T)scopeComponent;
         }

         if (_container.TryResolve(typeof(T), out var component)) {
             return (T)component;
         }

         return default;
     }
     ...

There isn’t a way to pass the Request Context instance in an IOC adapter, although the RequestContext behavior has remained unchanged for several years and I don’t see why the same RequestContext variable wouldn’t be available in a Global Request Filter if it’s set in a Pre Request Filter.

Are you saying the same instance no longer exists in the Global Request Filter?

GlobalRequestFilters.Add((req, resp, dto) => {
    var scope = RequestContext.Instance.Items["AutofacScope"] as ILifetimeScope;
});

Is the behavior different when using the HttpContext.Current context directly, i.e:

System.Web.HttpContext.Current.Items["AutofacScope"] = requestScope;

//....

var scope = System.Web.HttpContext.Current.Items["AutofacScope"] as ILifetimeScope;