Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Session.SetString method throws exception "IFeatureCollection has been disposed. Object name: 'Collection'. " in ASP.NET Core 3.1

I have a project written in ASP.NET Core 3.1.

I need to set data to Session in Singleton service:

 _session.SetString("some key", "some value");

I injected the session object from DI:

public OperatorService(ILogger<OperatorService> logger, 
                       ISession session,
                       IOptions<AppSettings> options)
{
     this._session = session; 
     this._logger = logger; 
     this._appSettings = options.Value;
}

I calls the my method as below:

public void ChangeOperatorStatus(StatusChangeRequest request)
{
     try
     {
         _session.SetString(request.Key, request.Value);
     }
     catch (Exception ex)
     {
          _logger.LogInformation($"Exception while changing status: {ex}"); 
     } 
} 

but I get the exception below :

IFeatureCollection has been disposed.\r\nObject name: 'Collection'. 

and I added some code to Startup.cs's ConfigureServices method:

services.AddHttpContextAccessor();

services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(20);
    options.Cookie.HttpOnly = true;
})
.AddDistributedMemoryCache();

And I added app.UseSession(); to the Configure method of Startup.cs.

I trid services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); and I get the session from httpContextAccessor.HttpContext.Session but I get the same error.

Please help me, thank you.

like image 635
Ramil Aliyev Avatar asked Jan 29 '20 08:01

Ramil Aliyev


People also ask

Which method throws an exception 'ifeaturecollection has been disposed'?

Session.SetString method throws exception "IFeatureCollection has been disposed. Object name: 'Collection'. " in ASP.NET Core 3.1 Bookmark this question. Show activity on this post. I have a project written in ASP.NET Core 3.1.

What is session in ASP NET Core?

Asp Net Core. Http Sets a String value in the ISession. The ISession. The key to assign. The value to assign. Stores user data while the user browses a web application. Session state uses a store maintained by the application to persist data across requests from a client. The session data is backed by a cache and considered ephemeral data.

How to throw system ObjectDisposedException when accessing httpcontext headers?

In the handler method, add access to HttpContext.Request.Headers. Create a REST client that sends many requests simultaneously. The result is that eventually, a System.ObjectDisposedException will be thrown upon accessing HttpContext.Request.Headers.

What is the object name of the ifeaturecollection?

c# - IFeatureCollection has been disposed. Object name: 'Collection'. going from controller to view - Stack Overflow IFeatureCollection has been disposed.


2 Answers

It took me a while to get this fixed. In my case, it was a 3.1 aspnetcore and it didn't worked until I turn the container function from

public async void OnPost

to

public async Task<IActionResult> OnPost

Looks like the HttpContext was disposed before it was used...

like image 90
Sergisoft Avatar answered Nov 16 '22 00:11

Sergisoft


An HttpContext is the context of a single request. It provides access to the request, response properties etc of that single request. You can't cache it, it becomes invalid once that request ends.

Session is another transient thing - it lives only as long as a single user session. There's at least one session for every user of a web app. Caching one of those sessions in a singleton guarantees that

  • The reference will become invalid after a while, when the session expires and
  • The singleton will use only that user's values, ignoring everyone else's. This is a bug in itself, and a great way to hack into an application.
  • If an administrator logs in, the Session object may apply the admin's settings alive to everyone for the next 20, 30 or 60 minutes.

That's why using a Session makes sense for per-request middleware, not Singleton services.

Correct usage of HttpContext

The Session can only be reached through the request's context, so getting the correct session means getting the correct HttpContext. The correct way to do this is explained in David Fowler's ASP.NET Core Guidance :

❌ BAD This example stores the HttpContext in a field then attempts to use it later.

    private readonly HttpContext _context;
    public MyType(IHttpContextAccessor accessor)
    {
        _context = accessor.HttpContext;
    }
    public void CheckAdmin()
    {
        if (!_context.User.IsInRole("admin"))
        {
            throw new UnauthorizedAccessException("The current user isn't an admin");
        }
    }

✅ GOOD This example stores the IHttpContextAccesor itself in a field and uses the HttpContext field at the correct time (checking for null).

   private readonly IHttpContextAccessor _accessor;
   public MyType(IHttpContextAccessor accessor)
   {
       _accessor = accessor;
   }

   public void CheckAdmin()
   {
       var context = _accessor.HttpContext;
       if (context != null && !context.User.IsInRole("admin"))
       {
           throw new UnauthorizedAccessException("The current user isn't an admin");
       }
   }

Use a Scoped service instead

Since a Singleton can't know what session to use. One option is to simply convert that service to a Scoped service. In ASP.NET Core, a request defines a scope. That's how controller actions and pipeline middleware get access to the correct HttpContext for each request.

Assuming the service is used by an action or middleware, perhaps the only change needed is to replace AddSingleton<ThatService> with AddScoped<ThatService>

Turning the tables, or Inversion of Control

Another option is for callers of that singleton should provide the session to it. Instead of using a cached session eg :

public void SetStatus(string status)
{
    _session.SetString(SessionKeys.UserStatus, "some value");
}

Ask for the session or HttpContext as a parameter :

public void SetStatus(string status,ISession session)
{
    session.SetString(SessionKeys.UserStatus, "some value");
}

And have callers pass the correct session to it

like image 33
Panagiotis Kanavos Avatar answered Nov 16 '22 02:11

Panagiotis Kanavos