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.
I guess one of the reasons could be that Asp.Net Core IServiceProvider does not allow Scoped Dependency to be injected inside Singleton class. That could be a major decision behind that. If things would have been scoped as you suggested then all the classes using it may have to be scoped. But interestingly enough once the request is served the HTTPContext becomes null.
Related
In my Asp.Net Core App I need a singleton service that I can reuse for the lifetime of the application. To construct it, I need a DbContext (from the EF Core), but it is a scoped service and not thread safe.
Therefore I am using the following pattern to construct my singleton service. It looks kinda hacky, therefore I was wondering whether this is an acceptable approach and won't lead to any problems?
services.AddScoped<IPersistedConfigurationDbContext, PersistedConfigurationDbContext>();
services.AddSingleton<IPersistedConfigurationService>(s =>
{
ConfigModel currentConfig;
using (var scope = s.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
currentConfig = dbContext.retrieveConfig();
}
return new PersistedConfigurationService(currentConfig);
});
...
public class ConfigModel
{
string configParam { get; set; }
}
What you're doing is not good and can definitely lead to issues. Since this is being done in the service registration, the scoped service is going to be retrieve once when your singleton is first injected. In other words, this code here is only going to run once for the lifetime of the service you're registering, which since it's a singleton, means it's only going to happen once, period. Additionally, the context you're injecting here only exists within the scope you've created, which goes away as soon as the using statement closes. As such, by the time you actually try to use the context in your singleton, it will have been disposed, and you'll get an ObjectDisposedException.
If you need to use a scoped service inside a singleton, then you need to inject IServiceProvider into the singleton. Then, you need to create a scope and pull out your context when you need to use it, and this will need to be done every time you need to use it. For example:
public class PersistedConfigurationService : IPersistedConfigurationService
{
private readonly IServiceProvider _serviceProvider;
public PersistedConfigurationService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task Foo()
{
using (var scope = _serviceProvider.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
// do something with context
}
}
}
Just to emphasize, again, you will need to do this in each method that needs to utilize the scoped service (your context). You cannot persist this to an ivar or something. If you're put off by the code, you should be, as this is an antipattern. If you must get a scoped service in a singleton, you have no choice, but more often than not, this is a sign of bad design. If a service needs to use scoped services, it should almost invariably be scoped itself, not singleton. There's only a few cases where you truly need a singleton lifetime, and those mostly revolve around dealing with semaphores or other state that needs to be persisted throughout the life of the application. Unless there's a very good reason to make your service a singleton, you should opt for scoped in all cases; scoped should be the default lifetime unless you have a reason to do otherwise.
Although Dependency injection: Service lifetimes documentation in ASP.NET Core says:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
But in your case this is not the issue. Actually you are not resolving the scoped service from singleton. Its just getting an instance of scoped service from singleton whenever it requires. So your code should work properly without any disposed context error!
But another potential solution can be using IHostedService. Here is the details about it:
Consuming a scoped service in a background task (IHostedService)
Looking at the name of this service - I think what you need is a custom configuration provider that loads configuration from database at startup (once only). Why don't you do something like following instead? It is a better design, more of a framework compliant approach and also something that you can build as a shared library that other people can also benefit from (or you can benefit from in multiple projects).
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
var persistentConfigBuilder = new ConfigurationBuilder();
var connectionString = builtConfig["ConnectionString"];
persistentStorageBuilder.AddPersistentConfig(connectionString);
var persistentConfig = persistentConfigBuilder.Build();
config.AddConfiguration(persistentConfig);
});
}
Here - AddPersistentConfig is an extension method built as a library that looks like this.
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddPersistentConfig(this IConfigurationBuilder configurationBuilder, string connectionString)
{
return configurationBuilder.Add(new PersistentConfigurationSource(connectionString));
}
}
class PersistentConfigurationSource : IConfigurationSource
{
public string ConnectionString { get; set; }
public PersistentConfigurationSource(string connectionString)
{
ConnectionString = connectionString;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new PersistentConfigurationProvider(new DbContext(ConnectionString));
}
}
class PersistentConfigurationProvider : ConfigurationProvider
{
private readonly DbContext _context;
public PersistentConfigurationProvider(DbContext context)
{
_context = context;
}
public override void Load()
{
// Using _dbContext
// Load Configuration as valuesFromDb
// Set Data
// Data = valuesFromDb.ToDictionary<string, string>...
}
}
To improve the session handling in our application, we wanted to create a wrapper class which manages session access via properties (to prevent the common issues when working with strings).
Something like this:
public class Wrapper
{
public string Username
{
get { return HttpContext.Current.Session.GetString("username"); }
set { HttpContext.Current.Session.SetString("username", value); }
}
}
There are quite a few examples of how to implement this in classic ASP.Net, but we weren't able to port them to ASP.Net Core. The only other question about a session wrapper in ASP.Net Core (that I could find) is using a Singleton, which we didn't find suitable for our case.
Our problem comes down to two main issues:
How to wrap and access the current Session inside of another class properly (avoiding anti-patterns and security issues)
How to share the class across our application
We decided to use the dependency injection functionality of ASP.Net Core, since it seemed perfectly suitable for the job. To access the session inside of a service the IHttpContextAccessor is necessary. As stated in David Fowler's ASP.NET Core Diagnostic Scenarios it is recommended to not store the IHttpContextAccessor.HttpContext in a field, however using a scoped service should mitigate this issue, since it gets created for each request.
Our setup
Here we use the HttpContextAccessor to reference the current session. Access to the session is restricted through the use of properties (the strings could further be replaced with constants).
public class SessionService
{
private readonly ISession _session;
public SessionService(IHttpContextAccessor accessor)
{
_session = accessor.HttpContext.Session;
}
public string Username
{
get { return _session.GetString("username"); }
set { _session.SetString("username", value); }
}
}
The session wrapper then is configured as a scoped service inside of Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
// ...
// Register the service we use for session handling.
services.AddScoped<SessionService>();
}
Last but not least the service is injected into our page model and used to access the session through its property.
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly SessionService _session;
public IndexModel(ILogger<IndexModel> logger, SessionService session)
{
_logger = logger;
_session = session;
}
public void OnPost()
{
// Set the session variable.
_session.Username = "foo#bar";
}
}
I am posting the answer in hopes of getting feedback on my solution and also providing a reference point for others struggling with this.
I am using ASP.NET Core. How can I use session variables in a static method?
In ASP.NET, this looked like this:
protected static string AssignSession()
{
return HttpContext.Current.Session["UserName"].ToString();
}
protected void Page_Load(object sender, EventArgs e)
{
Session["UserName"] = "super user";
}
When I try that in ASP.NET Core, I get the following error:
An object reference is required for the non-static field, method,
or property 'ControllerBase.HttpContext'.
The answer is generally: You don’t.
In ASP.NET Core, you pretty much avoid static code. Instead, ASP.NET Core uses dependency injection to make services available as dependencies and to control their lifetime.
A static utility class in ASP.NET would probably translate to a singleton service in ASP.NET Core. Using that is very straightforward; you start by creating a non-static service that does whatever you want to do. Since this is using dependency injection, you can just depend on other services as well:
public class MyService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void SetSomeSessionValue(string value)
{
var httpContext = _httpContextAccessor.HttpContext;
httpContext.Session["example"] = value;
}
}
You can do whatever you want there. The IHttpContextAccessor is used to retrieve the current HttpContext.
Then, you need to register your service with the dependency injection container. You do that in the ConfigureServices method in your Startup.cs:
services.AddSingleton<MyService>();
// we also add the HttpContextAccessor, in case it wasn’t already (implicitly) registered before
services.AddHttpContextAccessor();
And now, you can depend on this MyService within controllers or other services by simply adding it as a constructor argument:
public class HomeController
{
private readonly MyService _myService;
public HomeController(MyService myService)
{
_myService = myService;
}
public IActionResult Index()
{
_myService.SetSomeSessionValue("test");
return View();
}
}
Now, you have a non-static service that has clear dependencies and which you can test properly.
That all being said, many constructs already have access to the current HttpContext and as such to the session. For example, in controllers, Razor pages, or even Razor views, you can just access the HttpContext directly as it is an instance variable.
So if you are not building some reusable utility code, you don’t actually need to create a service for this. You could just for example create a (non-static) utility method within your controller that then accesses the HttpContext directly.
The UnityMvcActivator is called right out of the gate when starting my MVC application, and it instantiates, configures, and sets the container to the DependencyResolver:
DependencyResolver.SetResolver(new UnityDependencyResolver(UnityConfig.Container));
Which immediately registers all the types via:
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterMvcComponents();
}
...but I'm trying to register types that aren't created until a little further down the road:
public static IUnityContainer RegisterMvcComponents(this IUnityContainer container)
{
var lifetimeManager = new HierarchicalLifetimeManager();
container.RegisterInstance<HttpSessionStateBase>(
new HttpSessionStateWrapper(HttpContext.Current.Session), lifetimeManager);
container.RegisterInstance<HttpContextBase>(
new HttpContextWrapper(HttpContext.Current), lifetimeManager);
container.RegisterInstance<HttpServerUtilityBase>(
new HttpServerUtilityWrapper(HttpContext.Current.Server), lifetimeManager);
container.RegisterInstance(HttpContext.Current.User.Identity, lifetimeManager);
return container;
}
I can't get the container back from the DependencyResolver when I finally make it to my OWIN Startup class - which is where all the other initialization is taking place - so how can I register these types?
EDIT:
Thinking I was clever, I tried adding some post-start action to the activator by adding this assembly directive and moving my configuration method call to the newly created method:
[assembly: WebActivatorEx.PostApplicationStartMethod(
typeof(CCCS.Admin.Web.Ui.UnityMvcActivator),
nameof(CCCS.Admin.Web.Ui.UnityMvcActivator.PostStart))]
public static void PostStart() => UnityConfig.Container.RegisterMvcComponents();
... and that got me halfway, but the User and Session still aren't available.
This is more of an XY problem related to your design, as all the HttpContext related members wont be available at startup.
You would better off creating abstractions to defer the access to those implementation concerns.
public interface IHttpContextAccessor {
HttpContextBase HttpContext { get; }
}
public class HttpContextProvider : IHttpContextAccessor {
public virtual HttpContextBase HttpContext {
get {
return new HttpContextWrapper(HttpContext.Current);
}
}
}
Now all those registrations can be replaced with the one abstraction which would provide access to all the other related types.
public static IUnityContainer RegisterMvcComponents(this IUnityContainer container) {
var lifetimeManager = new HierarchicalLifetimeManager();
container.RegisterType<IHttpContextAccessor, HttpContextProvider>(lifetimeManager);
return container;
}
Note that the container should ideally only be accessed in the composition root of the application and not passed around as a dependency. That is seen as a code smell and an indicator that the design should be reviewed and refactored if possible.
When access is needed to HttpContext related members it is now a matter of injecting the accessor
private readonly IHttpContextAccessor accessor;
public MyDependent(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public void SomeMethodAccessedInAnAction() {
var context = access.HttpContext; // HttpContextBase
var session = context.Session; // HttpSessionStateBase
var server = context.Server; // HttpServerUtilityBase
var user = context.User; // IPrincipal
//...
}
In ASP.NET Core its very easy to access your memory cache from a controller
In your startup you add:
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
}
and then from your controller
[Route("api/[controller]")]
public class MyExampleController : Controller
{
private IMemoryCache _cache;
public MyExampleController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
[HttpGet("{id}", Name = "DoStuff")]
public string Get(string id)
{
var cacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromHours(1));
_cache.Set("key", "value", cacheEntryOptions);
}
}
But, how can I access that same memory cache outside of the controller. eg. I have a scheduled task that gets initiated by HangFire, How do I access the memorycache from within my code that starts via the HangFire scheduled task?
public class ScheduledStuff
{
public void RunScheduledTasks()
{
//want to access the same memorycache here ...
}
}
Memory cache instance may be injected to the any component that is controlled by DI container; this means that you need configure ScheduledStuff instance in the ConfigureServices method:
public void ConfigureServices(IServiceCollection services) {
services.AddMemoryCache();
services.AddSingleton<ScheduledStuff>();
}
and declare IMemoryCache as dependency in ScheduledStuff constructor:
public class ScheduledStuff {
IMemoryCache MemCache;
public ScheduledStuff(IMemoryCache memCache) {
MemCache = memCache;
}
}
I am bit late here, but just wanted to add a point to save someone's time. You can access IMemoryCache through HttpContext anywhere in application
var cache = HttpContext.RequestServices.GetService<IMemoryCache>();
Please make sure to add MemeoryCache in Startup
services.AddMemoryCache();
There is another very flexible and easy way to do it is using System.Runtime.Caching/MemoryCache
System.Runtime.Caching/MemoryCache:
This is pretty much the same as the old day's ASP.Net MVC's HttpRuntime.Cache. You can use it on ASP.Net CORE without any dependency injection, in any class you want to. This is how to use it:
// First install 'System.Runtime.Caching' (NuGet package)
// Add a using
using System.Runtime.Caching;
// To get a value
var myString = MemoryCache.Default["itemCacheKey"];
// To store a value
MemoryCache.Default["itemCacheKey"] = myString;