2

I was following this answer, how to properly start new thread in ASP.NET Core 2.0 Controller method.
I need this because I have API that writes something to database (returns result back to user) and at the same time initiate a long running background task (some excel calculations), that are returned back to user via SignalR Core

I am using OpenIddict for authorization/authentication with JWT token.

Problem is that I need to get IHttpContextAccessor so I can get logged in user id:

var serviceScopeFactory = this.ServiceProvider.GetService< IServiceScopeFactory>();
Task.Factory.StartNew(() =>
{
    using (var scope = serviceScopeFactory.CreateScope())
    {
        Thread.Sleep(3000);     //Just for testing to be sure, API request is completed.
        var services = scope.ServiceProvider.GetService<IHttpContextAccessor>();
        var claims = services2.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier);
        var loggedInUserId = claims != null ? Int32.Parse(claims.Value) : (int?)null;

        var res = ProcessExcel();
        signalrServices.Send(loggedInUserId, res);
    }
});

I really need to get ClaimsPrincipal. Someone may think problem is solveable with local variable userId, which is set before starting new thread. But in real application, many classes are created through dependency injection and one local variable would not solve my problem.

Problem is that services.HttpContext.User.Identity.Claims count is 0 and so userId is null.

How can I get correct UserClaims when using threading with IServiceScopeFactory?

Edited:

I tried like this:

public IActionResult DoSomething(string lang, int id)
{
    DoSomething();

    //Process in new thread
    Task.Factory.StartNewWithUser(this.ServiceProvider, async scope =>
    {
        await scope.ServiceProvider.GetService<LiveCalculations>().Add(lang, id);
    });

    return new JsonResult(true);
}    

public static Task StartNewWithUser(this TaskFactory factory, IServiceProvider serviceProvider, Func<IServiceScope, Task> action)
{
    var scopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
    var contextAccessor = serviceProvider.GetService<IHttpContextAccessor>();
    var user = contextAccessor.HttpContext.User.Clone();

    return Task.Factory.StartNew(() =>
    {
        using (var scope = scopeFactory.CreateScope())
        {
            Thread.CurrentPrincipal = user;             //https://forums.asp.net/t/1829657.aspx?+How+to+set+HttpContext+User+Identity+for+an+application+manually+
            scope.ServiceProvider.GetService<IHttpContextAccessor>().HttpContext.User = user;
            action(scope).Wait();
        }
    });
}

Claims.Count is 15 but at some random point in future Claims.Count become 0. Usually after some awaited function call. (Not always the first).

Bad solution:

The only solution I found (and I really don't like it) is to create new HttpRequest:

public string GetAccessToken()
{
    var bearer = this.Request.Headers["Authorization"].FirstOrDefault();
    if (string.IsNullOrEmpty(bearer))
        return "";
    return bearer.Substring("Bearer ".Length);
}

public IActionResult DoSomething(int id)
{
    DoSomeWork();

    //Instead creating new thread
    var service = this.ServiceProvider.GetService<RequestService>();
    string url = $"{service.Scheme}://{service.Host}/API/excel/";
    HttpClient client = new HttpClient();
#pragma warning disable 4014
    client.PutAsync(url + $"PrceossExcel/{id}?access_token={accessToken}", null);    //notice without await
#pragma warning restore 4014

    return new JsonResult(true);
}

It is not logic to me to create new Http Request just to get correct User (Identity) in new thread. I would really like to get a working solution with creating new thread.

Makla
  • 9,899
  • 16
  • 72
  • 142
  • Why does background job need to access `IHttpContextAccessor`? If you need to access user Id associated with the job, you could save them together in a persistent storage. – Win May 14 '18 at 14:58
  • Background job not, but classes created through DI in background process. – Makla May 14 '18 at 17:59
  • 1
    This happened before too: https://stackoverflow.com/questions/8109526/the-cross-thread-usage-of-httpcontext-current-property-and-related-things. I/You need to check what's the implementation of `IHttpContextAccessor`, but i bet that for cross-thread you need your own implementation. – Razvan Dumitru May 15 '18 at 06:40

0 Answers0