RequestContexts

Hey Mythz,

Happy new year and all that.

Quick question - I’m currently using the OrmLiteConfig.InsertFilter / UpdateFilter hooks to perform auditing. When using an Asp.net host I’m using the RequestContext.Instance.Items to store the CurrentUserId from the session as part of a global filter and this can then be picked up in the filter.

However I want to do a similar things when the request originates from the MqServer, I have the UserId in the request items, but the problem is RequestContext is not relevant as its not Asp.Net originated. Therefore I wondering if there is a static context I can use to store the UserId when the request originates from the Mq Server? Or alternatively can I can get at the current request from the Ormlite filters?

Dan

1 Like

Ideally I’d use IRequest.Items to store items for the current request for both HTTP + MQ Requests but you should still be able to use RequestContext.Instance for MQ Requests, but it will fallback to using ThreadStatic.

Hmm interesting - should it be using AsyncLocal in the most recent release. I’m not on core yet though.

Dan

AsyncLocal is only available from .NET 4.6, we’re using it in .NET Core builds but for the .NET Framework we need to support .NET 4.5

The reason it wasn’t working for the MQ is that a message was waiting in the queue and I was starting the MQ server during the initialization which meant that this was fired.

  if (!ServiceStackHost.IsReady())
            return new Dictionary<object, object>();

However I’ve just noticed another small problem is that if an exception occurs during a normal service call it seems the RequstContext is lost? Is this behavior by design?

Ahh ha, I think it’s down to the continuations in the servicerunner…

 return taskResponse.ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    var ex = task.Exception.UnwrapIfSingleException();

                    //Async Exception Handling
                    var result = HandleException(request, requestDto, ex);

                    if (result == null)
                        return ex;

                    return result;
                }

Its looses the synchronization context. This would need ContinueWith(…, TaskScheduler.FromCurrentSynchronizationContext()) specified.

Obviously on MQ calls there is no SyncronizationContext so it would need to be created when
SynchronizationContext.Current is null.

I’ll go ahead and implement this in my own IServiceRunner, but is there scope to do some work on an async service runner in the future?

Hi Mythz,

I did this - however still not quite there and looking for some advise. The reason I wan’t to preserve data in the RequestContext is due to logging. I’m storing the current correlation Id into the RequestContext so I can hydrate any logging requests (Serilog enricher) with this information.

I have implemented a custom service runner (as mentioned above) which includes TaskScheduler.FromCurrentSynchronizationContext() on the continuation and the context is preserved down the ServiceExceptionHandlers which is good. However where an error is also logged internally for example in MessageHandler the context is lost. Looking at the code appears that their are continuations in a few places ie / ServiceGateway / ServiceController etc.

Obviously I want to keep any modification to a minimum - so look for some advise on this issue?

I suppose I could just switch off service stack logging but this does not feel right.

Dan

Hi Mythz,

Any thoughts on this?

Dan

Not sure what other options there are, where possible I’d be storing any context in IRequest.Items so it’s attached with the request.

If it helps I could wrap the ContinueWith so it’s overridable and change the code in ServiceStack to use:

HostContext.Async.ContinueWith(req, taskResponse, task => {
    ...
})

Unfortunately there are several ContinueWith overloads used that would need to be overridden which I’d prefer to be maintained in a different class to avoid polluting the AppHost, which could be configured with:

class AsyncContext
{
        public virtual Task ContinueWith(IRequest req, Task task, Action<Task> fn)
        {
            return task.ContinueWith(fn);
        }

        public virtual Task<Task<object>> ContinueWith(IRequest req, Task<object> task, Func<Task<object>, Task<object>> fn)
        {
            return task.ContinueWith(fn);
        }

        public virtual Task<object> ContinueWith(IRequest req, Task task, Func<Task, object> fn)
        {
            return task.ContinueWith(fn);
        }
}

HostContext.Async = new AsyncContext();

Which you could override with your own custom impl, e.g:

HostContext.Async = new AsyncWithSyncContext();

Let me know if that would help?

Mythz,

You are the man. This hook would hit the nail on the head. If I can maintain the context throughout then problem solved.

I presume the ContinueWith wrapper would be referenced from all places throughout the chain including ServiceGateway, ServiceController and ServiceRunner?

Dan

I’ve rewritten all the ContinueWith calls to use an overridable AsyncContext in this commit.

Please send a PR if you need any tweaks to help what you need.

This change is available from v4.5.5+ that’s now available on MyGet.

Hey Mythz,

Sweet - that’s amazing. I’ve just pulled from myGet and cleared package cache.

Can’t see Async in HostContext?

Dan

Doesn’t sound like your NuGet packages cache was cleared properly.

Downloading and inspecting ServiceStack.dll directly from MyGet:
https://www.myget.org/F/servicestack/api/v2/package/ServiceStack/4.5.5

Shows it’s the latest version which contains the new AsyncContext class:

Sorry about that I missed the local caches. Anyway good work - this is great.

I’ll let you know how I get on.

Dan