Home > Software engineering >  .Net 6 - Razor Pages - Problems with HttpContext.Session
.Net 6 - Razor Pages - Problems with HttpContext.Session

Time:06-09

I'm working on a .Net6 razor pages project and I'm usign session to store variables. I have a page which can edit an XML "document" (a DespatchAdvise - for your info) and I store the model object in session, in order to manage CRUD operations on sublists of this document.

Here's my Startup.cs where I configure session

 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 => false;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        //*** Other settings omitted ***

        services.AddDistributedMemoryCache();
        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromMinutes(Configuration.GetValue<int>("SessionTimeoutInMinutes"));
            options.Cookie.HttpOnly = true;
            options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
            options.Cookie.IsEssential = true;
            options.Cookie.Name = "_aspnetCoreSession";
        });
        services.AddHttpContextAccessor();

        //*** Other settings omitted ***
    }

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment() || env.IsStaging())
        {
            app.UseDeveloperExceptionPage();
            RegisteredServicesPage(app); //create a custom page with every service registered
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();           
        app.UseStaticFiles();
        app.UseCookiePolicy();
        app.UseRouting();           
        app.UseAuthentication();            
        app.UseSession();             
        app.UseAuthorization();                       
        app.UseMvc();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }

I have a class that creates extensions methods for session

public static class SessionUtils
{
    public static void SetObjectAsJson(this ISession session, string key, object value)
    {
        session.SetString(key, JsonConvert.SerializeObject(value));
        session.CommitAsync().Wait(); //added to see if something changed (no success)
    }

    public static T GetObjectFromJson<T>(this ISession session, string key)
    {
        session.LoadAsync().Wait();//added to see if something changed (no success)

        var value = session.GetString(key);
        return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value, new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Replace });            
    }
}

The two methods CommitAsync and LoadAsync doesn't helped me a lot :(

Now here's my problem... In my page I can edit data about "IdentityDocumentReference", and I save these data in session, but sometimes data is not saved. It is very strange because is not deterministic (in my opinion). Sometimes data is saved, sometimes not.

Note: Load and Save methods are handled using AJAX. Load by DataTables ajax call and Save with a normal AJAX call

Here's the code that returns the object list to datatables and the method that saves in session.

public JsonResult OnPostLoadDriversDA()
{
    Utils_CommonResponse resp = new Utils_CommonResponse();
    List<DespatchAdviceAdditionaDocumentReference> ListIdDocumentRefDriver = new List<DespatchAdviceAdditionaDocumentReference>();

    try
    {
        DespatchAdviceDettaglio = HttpContext.Session.GetObjectFromJson<DespatchAdviceDettaglio>("DespatchAdviceDettaglio");
        List<DocumentReferenceType> IdDriver = DespatchAdviceDettaglio.DespatchAdvice.Shipment.Consignment[0].CarrierParty.Person[0].IdentityDocumentReference;

        if (IdDriver.Count > 0 && String.IsNullOrEmpty(IdDriver.First().ID.Value))
        { 
            //Here I reset the list because I have an empty object to create the interface but for datatables must be an empty list
            IdDriver = new List<DocumentReferenceType>();       
            DespatchAdviceDettaglio.DespatchAdvice.Shipment.Consignment[0].CarrierParty.Person[0].IdentityDocumentReference = IdDriver;
            
            //Here the object IdentityDocumentReference has 0 items (IdDriver is a new List - the problem is on save method)
            HttpContext.Session.SetObjectAsJson("DespatchAdviceDettaglio", DespatchAdviceDettaglio);            
        }

        foreach (DocumentReferenceType drf in IdDriver)
        {
            ListIdDocumentRefDriver.Add(new DespatchAdviceAdditionaDocumentReference()
            {
                ID = drf.ID.Value,
                TipoDocumento = drf.DocumentType.Value
            });
        }

        return new JsonResult(new
        {
            DataTablesRequest.Draw,
            recordsFiltered = ListIdDocumentRefDriver.Count,
            data = ListIdDocumentRefDriver
        });
    }
    catch (Exception exc)
    {
        _logger.LogError($"{exc.Message} - {exc.StackTrace}");
        resp.Esito = false;
        resp.Messaggio = exc.Message;
        return new JsonResult(resp);
    }
}

Method that save data in session

    public JsonResult OnPostSaveDriveDA([FromBody] SaveDriver IncomingData)
{
    Utils_CommonResponse resp = new Utils_CommonResponse();
    try
    {
        DespatchAdviceDettaglio = HttpContext.Session.GetObjectFromJson<DespatchAdviceDettaglio>("DespatchAdviceDettaglio");
        if (IncomingData.IdDriver == -1)
        {     
            //Here, after loading "DespatchAdviceDettaglio" from session, SOMETIMES the list IdentityDocumentReference has 1 element (the empty one)                              
            DespatchAdviceDettaglio.DespatchAdvice.Shipment.Consignment[0].CarrierParty.Person[0].IdentityDocumentReference.Add(IncomingData.DocRefDriver);
            
        }
        else
        { //edit
            DespatchAdviceDettaglio.DespatchAdvice.Shipment.Consignment[0].CarrierParty.Person[0].IdentityDocumentReference[IncomingData.IdDriver] = IncomingData.DocRefDriver;
        }
        HttpContext.Session.SetObjectAsJson("DespatchAdviceDettaglio", DespatchAdviceDettaglio);

        resp.Esito = true;
        resp.Messaggio = "";
    }
    catch (Exception exc)
    {
        resp.Esito = false;
        resp.Messaggio = exc.Message;
    }
    return new JsonResult(resp);
}

As I wrote in the comment above, looks like in OnPostSaveDriveDA the object I get from session is the "old one". Then, the new driver is added to the list. But when I reload the list, I lose every items because the first is obviously the empty one so the list is reset.

This is blowing my mind because it doesn't happens every time. It looks like sometimes session doesn't store data after my "SetObjectAsJson".

Did someone have the same problem or knows how to solve it and tell me why this happens?

Thanks to everyone who can help me.

CodePudding user response:

Try to check the session Id and the hash code is the same for all requests

CodePudding user response:

I finally found the solution.

The problem was due to async ajax squence calls that, depending on the order, overwrite the session value I need to store. If a call comes in parallel with the one I need, it write a wrong value (because I get and set the entire object from/to session - DespatchAdvice in this case).

A solution could be working with Partial JSON fragments as can be seen here and working directly on the property you need instead of the whole object. Another solution may be to set ajax as synchronous in this way:

$.ajaxSetup({
    async: false
});
  • Related