30

I'm trying to access session storage in a razor view for a .net core 2.0 project. Is there any equivalent for @Session["key"] in a .net 2.0 view? I have not found a working example of how to do this - I am getting this error using the methods I have found:

An object reference is required for the non-static field, method, or propery HttpContext.Session

View:

@using Microsoft.AspNetCore.Http

[HTML button that needs to be hidden/shown based on trigger]

@section scripts {
<script>
    var filteredResults = '@HttpContext.Session.GetString("isFiltered")';
</script>
}

Startup.cs:

public void ConfigureServices(IServiceCollection services)
    {
        services.AddSession(options => {
            options.IdleTimeout = TimeSpan.FromMinutes(30);
        });

        services.AddMvc();

        // Added - uses IOptions<T> for your settings.
        // Added - replacement for the configuration manager
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        //exception handler stuff
        //rewrite http to https
        //authentication
        app.UseSession();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
Shyju
  • 214,206
  • 104
  • 411
  • 497
Ella
  • 465
  • 1
  • 5
  • 14
  • Why would you access session directly from the view? The ViewModel in most cases should contain all the relevant data for the view. The controller should populate the ViewModel from session... – Erik Philips Oct 24 '17 at 23:00
  • After rethinking this for a few days, I realized it wasn't as complex a situation as I originally thought, and just populated the view model from session storage in the controller. – Ella Nov 02 '17 at 23:18

4 Answers4

46

You can do dependency injection in views, in ASP.NET Core 2.0 :)

You should inject IHttpContextAccessor implementation to your view and use it to get the HttpContext and Session object from that.

@using Microsoft.AspNetCore.Http
@inject IHttpContextAccessor HttpContextAccessor
<script>
   var isFiltered = '@HttpContextAccessor.HttpContext.Session.GetString("isFiltered")';
   alert(isFiltered);
</script>

This should work assuming you have the relevant code in the Startup.cs class to enable session.

public void ConfigureServices(IServiceCollection services)
{
    services.AddSession(s => s.IdleTimeout = TimeSpan.FromMinutes(30));
    services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseSession(); 

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");

    });
}

To set session in a controller, you do the same thing. Inject the IHttpContextAccessor to your controller and use that

public class HomeController : Controller
{
   private readonly ISession session;
   public HomeController(IHttpContextAccessor httpContextAccessor)
   {
      this.session = httpContextAccessor.HttpContext.Session;
   }
   public IActionResult Index()
   {
     this.session.SetString("isFiltered","YES");
     return Content("This action method set session variable value");
   }
}

Use Session appropriately. If you are trying to pass some data specific to the current page, (ex : Whether the grid data is filtered or not , which is very specific to the current request), you should not be using session for that. Consider using a view model and have a property in that which you can use to pass this data. You can always pass these values to partial views as additional data through the view data dictionary as needed.

Remember, Http is stateless. When adding stateful behavior to that, make sure you are doing it for the right reason.

Shyju
  • 214,206
  • 104
  • 411
  • 497
  • I see. These days you can cache things in browser as well and send it via ajax call. But i cannot tell you how/what to do without knowing your specific use case. Like i mentioned, I am not saying it is bad. But make sure you are using it for the right reason. – Shyju Oct 24 '17 at 23:24
  • I thought about it for a few days and realized I was over-complicating the situation. I think session is still useful in this specific situation, but I could pass the values to the view model in the controller since it was only affecting the initial state of the page in any case. Thanks for your help, though. – Ella Nov 02 '17 at 23:24
  • 3
    TLDR: services.AddSingleton(); When using the the latest dotnet version, read https://github.com/aspnet/Hosting/issues/793 – Warrick Feb 24 '18 at 06:26
  • 1
    It did not work for me. I had to add services.TryAddSingleton(); in ConfigureServices method (Startup.cs) https://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc – liotims Feb 28 '19 at 08:58
  • @liotims, that is because in above code it is injecting from HomeController constructor,,without that also we can directly use HttpContext.Session.SetString("key", "value") to set the session – user3501613 Jan 30 '23 at 11:25
35

put this at the top of the razor page

@using Microsoft.AspNetCore.Http;

then you can easily access session variables like that

<h1>@Context.Session.GetString("MyAwesomeSessionValue")</h1>

if you get null values , make sure you include that in your Startup.cs

& make sure that options.CheckConsentNeeded = context is set to false

For more information about CheckConsentNeeded check this GDPR

public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                //options.CheckConsentNeeded = context => true;
                options.CheckConsentNeeded = context => false;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddDistributedMemoryCache();

            services.AddSession(options =>
            {
                // Set session timeout value
                options.IdleTimeout = TimeSpan.FromSeconds(30);
                options.Cookie.HttpOnly = true;
            });
        }

Also make sure you are adding app.UseSession(); to your app pipeline in Configure function

for more info about Sessions in Asp.net Core check this link Sessions in Asp.net Core

tested on .net core 2.1

Mawardy
  • 3,618
  • 2
  • 33
  • 37
2

As others have mentioned, I think the real solution here is not to do this at all. I thought about it, and while I have a good reason for using the session, since the razor tags are only useful for the initial page load anyway it makes more sense to just populate the view model in the controller with the stored session values.

You can then pass the view model with the current session values to your view, and access your model instead. Then you don't have to inject anything into your view.

Ella
  • 465
  • 1
  • 5
  • 14
1

below code worked for me in .net 6

in Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
     services.AddSession(options => {
            options.IdleTimeout = TimeSpan.FromDays(1);
        });
    
     services.AddMvc().AddNewtonsoftJson(options =>
        {
            options.SerializerSettings.ContractResolver = new DefaultContractResolver();
        });
    }
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
       app.UseSession();
    }

in controller

    [HttpPost]
    public async Task<IActionResult> GetData([FromBody] IncomignRequest request)
    {
        if (request?.UserId != null)
        {
            HttpContext.Session.SetString("CurrentUser", request.UserId);
            return Json(true);
        }
        else
            return Json(false);
    }

in HTML

@using Microsoft.AspNetCore.Http
@inject IHttpContextAccessor HttpContextAccessor
<script>
var user =  @Json.Serialize(@HttpContextAccessor.HttpContext.Session.GetString("CurrentUser"))
</script>
user3501613
  • 596
  • 7
  • 28