Per-Request Dependency lifecyle: ReuseScope.Request

Some time back, (I cannot remember when) I was warned that ReuseScope.Request may not be reliable.

I am not sure what the status of ReuseScope.Request is now, but we are proceeding with ReuseScope.Request` and we are getting some strange intermittent behaviour that is very hard to pin down, and wondering if it might be related to this scope or not.

(I am simply trying to determine whether to look into this as a potential problem or look elsewhere, since we are having quite the problem identifying precisely what is causing our problem.

I have a dependency defined like this, which is injected into all our services:

    public class AuthSessionCurrentCaller : ICurrentCaller, IRequiresRequest
    {
        private IRequest currentRequest;
        private IAuthSession currentSession;

        public string Username
        {
            get
            {
                var sess = GetSession();
                if (sess == null)
                {
                    return null;
                }

                return sess.UserName;
            }
        }

        public string Id
        {
            get
            {
                var sess = GetSession();
                if (sess == null)
                {
                    return null;
                }

                return sess.UserAuthId;
            }
        }

        public string AccessToken
        {
            get
            {
                var sess = GetSession();
                if (sess == null)
                {
                    return null;
                }

                var tokens = sess.ProviderOAuthAccess.SafeList();
                if (!tokens.Any(tk => tk.Provider.EqualsIgnoreCase(OAuth2AccessTokenAuthProviderReader.Name)))
                {
                    return null;
                }

                return tokens.First(tk => tk.Provider.EqualsIgnoreCase(OAuth2AccessTokenAuthProviderReader.Name)).AccessToken;
            }
        }

        public IEnumerable<string> Roles
        {
            get
            {
                var sess = GetSession();
                if (sess == null)
                {
                    return Enumerable.Empty<string>();
                }

                return sess.Roles.Safe();
            }
        }

        public bool IsAuthenticated
        {
            get
            {
                var sess = GetSession();
                if (sess == null)
                {
                    return false;
                }

                return sess.IsAuthenticated;
            }
        }

        public bool IsInRole(string role)
        {
            return Roles.Any(r => r.EqualsIgnoreCase(role));
        }

        public IRequest Request
        {
            get { return this.currentRequest; }
            set
            {
                this.currentRequest = value;
                this.currentSession = null;
            }
        }

        private IAuthSession GetSession()
        {
            if (this.currentRequest == null)
            {
                return null;
            }

            if (this.currentSession == null)
            {
                this.currentSession = this.currentRequest.GetSession();
            }

            return this.currentSession;
        }
    }

Where ICurrentCaller is our own interface to identify the calling user of the service (i.e Id, Username, Roles, etc) , and IRequiresRequest is our own marker interface, that is used like this, so that we can inject the IRequest into it every request.

public override object OnPreExecuteServiceFilter(IService service, object request, IRequest httpReq, IResponse httpRes)
        {
            service.InjectRequestIntoServiceDependencies(httpReq);

            return base.OnPreExecuteServiceFilter(service, request, httpReq, httpRes);
        }

Where InjectRequestIntoServiceDependencies() enumerates all the public properties of a service (and all the sub-dependencies of those properties recursively) looking for any property that derives from IRequiresRequest, and when found, injects the instance of the IRequest into that property.

OK, so our singleton instance of AuthSessionCurrentCaller is registered (in AppHost) like this:

            appHost.Container.Register<ICurrentCaller>(x => new AuthSessionCurrentCaller())
                .ReusedWithin(ReuseScope.Request);

So all the services in our AppHost should be using a singleton instance of AuthSessionCurrentCaller , which should then at any given time, have an instance of the current IRequest that the web service is handling. (that’s the intention at least).

OK, so all fine and dandy, we think.

The ICurrentCaller instance is injected into all our services and used throughout the life cycle of every service operation.
But unfortunately we are seeing intermittently that the identity of the user appears to change (specifically AuthSessionCurrentCaller.Roles) sometimes changes during a single request for some reason, that we cannot figure out. (Very hard to repro)

I am just wondering if anyone can see an obvious issue with this setup, where we might not be getting different requests/sessions mixed up with each other?

For example, could the session values change during the service operation lifecycle (we certainly are not doing this after the request is Pre-Authenticated), or could things like having a SSE server (also registered in the same AppHost) be mucking with the the current Request or Session values?

I can’t say what the exactly the cause of this behavior, but there is a way to track it down. Your AuthSessionCurrentCaller should be created for every request, and AuthSessionCurrentCaller.Request property should be set only once during its life-cycle. So you can add assert that this.

    set
    {
        if (this.currentRequest != null)
             throw Exception("Request already set!");
        this.currentRequest = value;
        this.currentSession = null;
    }

Then if you hit this assert you can compare currentRequest and value and if they differ each other that is the cause of isuue.

for example, this could happen when registration code not

appHost.Container.Register<ICurrentCaller>(x => new AuthSessionCurrentCaller())

but mistyped as

appHost.Container.Register<ICurrentCaller>(new AuthSessionCurrentCaller())

If request always the same and is not overwritten during execution, then you should look into GetSession(). If you use your custom session and you should check that two different users use different objects when storing authentication data, otherwise they could mix-up session settings as you describe.

Thanks @xplicit, that assertion was a great idea.
(We don’t have a custom AuthSession, and we don’t do anything to the Session beyond populating it on the PreAuthenticate method of our AuthProvider as you would normally).

I changed the AuthSessionCurrentCaller like this:

        public IRequest Request
        {
            get { return this.currentRequest; }
            set
            {
                if (this.currentRequest != null
                    && this.currentRequest != value)
                {
                    throw new InvalidOperationException("The 'Request' property of the 'AuthSessionCurrentCaller' has been changed for the current instance. The 'Request' should only be set once for each instance of the 'AuthSessionCurrentCaller'");
                }

                this.currentRequest = value;
                this.currentSession = null;
            }
        }

It exposed the exception and the fact that the IRequest property is being set more than once on an instance of AuthSessionCurrentCaller, and with a different instance of an IRequest! which is unexpected. This could well be leading the problems we are seeing.

Now I have checked all cases of where the AuthSessionCurrentCaller is registered in the container, and declare all of them like this:

container.Register<ICurrentCaller>(x => new AuthSessionCurrentCaller())
                .ReusedWithin(ReuseScope.Request);

At this point I can only conclude that we have a singleton instance of AuthSessionCurrentCaller that is living longer than each request, or I don’t really understand what is really happening under the covers with ReusedWithin(ReuseScope.Request)

I wonder if anyone knows whether ReusedWithin(ReuseScope.Request) is not really working properly or how to use it properly. Or perhaps how we can have a single instance of dependency that is populated by the IRequest every request?

In this case I’d check which part of code changes Request property. I’d compare two stack traces: one on the first change and second when assertion exception is raised. Stack trace can be captured with System.Diagnostics.StackTrace class and saved to private field for later comparison.
With stack traces you can identify which part of code changed this property, and possible this will help to find the cause of the issue. For example, the instance of the class could be saved into the variable which is accessible between requests, and some code changed it from other request.