Search code examples
c#asp.net.netmultithreadingprincipal

Retaining principal inside queued background work item


I'm using ASP.Net Web API 2 / .Net 4.5.2.

I'm trying to retain the calling principal when queueing a background work item. To that end, I'm trying to:

Thread.CurrentPrincipal = callingPrincipal;

But when I do so, I get an ObjectDisposedException:

System.ObjectDisposedException: Safe handle has been closed

How do I keep the current principal inside the background work item?
Can I make a copy of the principal somehow?

public void Run<T>(Action<T> action)
{
    _logger.Debug("Queueing background work item");
    var callingPrincipal = Thread.CurrentPrincipal;
    HostingEnvironment.QueueBackgroundWorkItem(token =>
    {
        try
        {
            // UNCOMMENT - THROWS EXCEPTION
            // Thread.CurrentPrincipal = callingPrincipal;
            _logger.Debug("Executing queued background work item");
            using (var scope = DependencyResolver.BeginLifetimeScope())
            {
                var service = scope.Resolve<T>();
                action(service);
            }
        }
        catch (Exception ex)
        {
            _logger.Fatal(ex);
        }
        finally
        {
            _logger.Debug("Completed queued background work item");
        }
    });
}

Solution

  • Turns out ClaimsPrincipal now has a copy constructor.

    var principal = new ClaimsPrincipal(Thread.CurrentPrincipal);
    

    This appears to resolve the issue while retaining all of the identity and claims information. The complete function follows:

    public void Run<T>(Action<T> action)
    {
        _logger.Debug("Queueing background work item");
        var principal = new ClaimsPrincipal(Thread.CurrentPrincipal);
    
        HostingEnvironment.QueueBackgroundWorkItem(token =>
        {
            try
            {
                Thread.CurrentPrincipal = principal;
                _logger.Debug("Executing queued background work item");
                using (var scope = DependencyResolver.BeginLifetimeScope())
                {
                    var service = scope.Resolve<T>();
                    action(service);
                }
            }
            catch (Exception ex)
            {
                _logger.Fatal(ex);
            }
            finally
            {
                _logger.Debug("Completed queued background work item");
            }
        });
    }