I looked at how HttpContext being created in .NET Core. Then I found that there is a class called HttpContextFactory
which create and assign HttpContext
object into a HttpContext
property of HttpContextAccessor
class. And to use HttpContext object in our code, we inject IHttpContextAccessor to the constructor of the class that needs the object.
When I looked at the implementation of HttpContextAccessor, apparently its HttpContext property gets the HttpContext object value from a private AsyncLocal
variable and later on HttpContextAccessor is registered as Singleton.
https://github.com/aspnet/AspNetCore/blob/master/src/Http/Http/src/HttpContextAccessor.cs
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
namespace Microsoft.AspNetCore.Http
{
public class HttpContextAccessor : IHttpContextAccessor
{
private static AsyncLocal<HttpContextHolder> _httpContextCurrent = new AsyncLocal<HttpContextHolder>();
public HttpContext HttpContext
{
get
{
return _httpContextCurrent.Value?.Context;
}
set
{
var holder = _httpContextCurrent.Value;
if (holder != null)
{
// Clear current HttpContext trapped in the AsyncLocals, as its done.
holder.Context = null;
}
if (value != null)
{
// Use an object indirection to hold the HttpContext in the AsyncLocal,
// so it can be cleared in all ExecutionContexts when its cleared.
_httpContextCurrent.Value = new HttpContextHolder { Context = value };
}
}
}
private class HttpContextHolder
{
public HttpContext Context;
}
}
}
I'm curious, what is the benefit of doing it this way instead of using a Scope service? It seems to me both would make the object available within a request scope.
If it's a scope service, I reckon the HttpContextAccessor will look something like this
using System.Threading;
namespace Microsoft.AspNetCore.Http
{
public class HttpContextAccessor : IHttpContextAccessor
{
private HttpContextHolder _httpContextCurrent;
public HttpContext HttpContext
{
get
{
return _httpContextCurrent?.Context;
}
set
{
if (value != null)
{
_httpContextCurrent = new HttpContextHolder { Context = value };
}
}
}
private class HttpContextHolder
{
public HttpContext Context;
}
}
}
Then use it as scope service
services.TryAddScope<IHttpContextAccessor, HttpContextAccessor>();
I would like to know what are the advantages and disadvantages for each approach, so that I'll understand when to use Singleton with AsyncLocal or Scope when creating a library for my project.
As long as it is a singleton, the resolved IHttpContextAccessor
instance could be holded permanently by a singleton service and work properly, while it could cause problems if a singleton service resolves a scoped IHttpContextAccessor
.