How can I access session data in Asp.net MVC Core 7?

52 views Asked by At

I'm in the process of migrating a huge project from .net4.8 to .net7.

The solution is basically a tiny web core project called Investo, but it references a huge project called General. 99% of the logic is in General.

in General I have loads of calls to a function called GetUserID, this function (in asp.net 4.8) returns a parameter stored in the HttpContext.Current.Session. works great for 10 years.

But now when I call it from .net7, which removed the HttpContext.Current, I can't figure out how to access the state in arbitrary code.

I tried storing it in a global shared variable, but it seems to randomly get disposed or other random errors.

Here is the relevant part of my program.cs

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpContextAccessor();
builder.Services.AddSession();
builder.Services.AddControllersWithViews();
builder.Services.AddSingleton<WebData>();
builder.Services.AddScoped<WebData>();
var app = builder.Build();

app.UseStaticFiles();
app.UseRouting();
app.UseSession();

Core.Options.PersonID = () =>
{
    var ses = WebData.CurSession();
    if (ses == null) return null;
    var sessionData = ses.GetString("WebSession");
    if (sessionData == null) return null;
    var ws = JsonConvert.DeserializeObject<WebSession>(sessionData);
    return ws.PersonID;
};

app.MapGet("/Global/InvokeFunction", (string Name, string JsonInput, HttpContext httpContext) =>
{
    //return WebFunctions.InvokeFunction(Name, JsonInput);
    //var a = new WebData();
    WebData.WebContext = httpContext;
    //if(WebData.WebContext ==null)   WebData.WebContext = httpContext;
    var uid = Core.Functions.GetUserID();
    var ret = Web.WebFunctions.InvokeFunction(Name, JsonInput);
    var json = Utils.Extensions.ToSafeJson(ret);
    return Results.Content(json, "application/json");
});

app.Run();

Here is WebData:

public class WebData
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public WebData(IHttpContextAccessor httpContextAccessor) {
        _httpContextAccessor = httpContextAccessor; // never fires
    }
     
    static public HttpContext WebContext { get; set; }
    static public ISession? CurSession() {
        var cx = WebContext;
        if (cx == null) return null; //OK. maybe background threads etc.
        try {
            var ses = cx.Session; //this often fails with "object reference not set to instance of an object". even if its a try-catch. 
            return ses;
        }
        catch (Exception ex) {
            if (!ex.Message.StartsWith("IFeat")) {
                var b = 1;//happens often can figure out rhyme or reason why and when it happens. sometimes works sometimes not
            }
            var a = ex;
        }
        return null;
    }
}

The first 3 lines (regarding IHttpContextAccessor) I added since I read that it somehow is supposed to help, though in fact it never fires.

Please advise how I can access current session information down the pipeline of MapGet> InvokeFunction> my whole code base.

If necessary, I'd be glad to provide any additional information.

Thanks in advance!

Update 1

Removing AddSingleton and AddScoped seem to have no effect. To the contrary, I think I now have more errors.

2

There are 2 answers

0
  Nathan Paneth On BEST ANSWER

My solution for this was

  1. Changed the helper Web Data to a Static class & methods so the WebData looks:
public static class WebData
{
    private static IHttpContextAccessor _httpContextAccessor;
    public static void Init(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }
    public static HttpContext? WebContext()
    {
        if (_httpContextAccessor != null)
            return _httpContextAccessor.HttpContext;
        return null;
    }
    public static ISession? CurSession()
    {
        //var cx = _httpContextAccessor.HttpContext;
        var cx = WebContext();
        if (cx == null) return null;
        try
        {
            var ses = cx.Session;
            return ses;
        }
        catch (Exception ex)
        {
            //if (!ex.Message.StartsWith("IFeat"))
            //{
            //    var b = 1;
            //}
            //var a = ex;
        }
        return null;
    }
}
  1. Added WebData.Init(builder.Services.BuildServiceProvider().GetRequiredService<IHttpContextAccessor>()); to Program.cs.
2
Qiang Fu On

You used static variable static public HttpContext WebContext { get; set; }; This is somehow equal to singleton lifetime.
So you should delete following codes which messing up the variable lifetime.

builder.Services.AddSingleton<WebData>();
builder.Services.AddScoped<WebData>();

This kind of service register pattern is used for dependency injection, which wouldn't work for static method or class. So the IHttpContextAccessor httpContextAccessor won't fired at all.