Accessing HttpSessionState (HttpContext.Current.Session) from another thread or tricks?

VSP picture VSP · Nov 25, 2010 · Viewed 18.1k times · Source

We have a web site which implements a central HttpSessionState management in App_Code like this:

public static class CurrentSession
{
    public static HttpSessionState Session
    {
        get
        {
            return HttpContext.Current.Session;
        }
    }

    public static bool Exists
    {
        get
        {
            return Session != null ? true : false;
        }
    }
    public static ControlUsu user
    {
        get
        {
            return (ControlUsu)Session["currentuser"];
        }

        set
        {
            Session["currentuser"] = value;
        }
    }
    public static OdbcConnection connection
    {
        get
        {
            return (OdbcConnection)Session["currentconnection"];
        }
        set
        {
            Session["currentconnection"] = value;
        }
    }
    public static OdbcCommand command
    {
        get
        {
            return (OdbcCommand)Session["currentcommand"];
        }
        set
        {
            Session["currentcommand"] = value;
        }
    }
    public static DataTable datatable
    {
        get
        {
            return (DataTable)Session["currentdatatable"];
        }
        set
        {
            Session["currentdatatable"] = value;
        }
    }
    public static OdbcDataAdapter dataadapter
    {
        get
        {
            return (OdbcDataAdapter)Session["currentdataadapter"];
        }
        set
        {
            Session["currentdataadapter"] = value;
        }
    }
    public static Table tablatemp
    {
        get
        {
            return (Table)Session["tablatemp"];
        }
        set
        {
            Session["tablatemp"] = value;
        }
    }

    public static void Init()
    {
        user= new ControlUsu();
        connection= new OdbcConnection();
        command= new OdbcCommand();
        datatable = new DataTable();
        dataadapter = new OdbcDataAdapter();
        tablatemp = new Table();
        //SessionActual.conexion.ConnectionTimeout = 0;
    }
}

Functions class that uses it (for example):

public class Funx
{
    public DataTable QuerySQLDT(string SQLx)
    {
        try
        {
            CurrentSession.connection.Open();
        }
        catch (Exception ex)
        {
            ServicioTecnico.EnviarMailError("Error openning connection", ex);
            HttpContext.Current.Response.Redirect("SesionExpirada.aspx", true);
        }
        try
        {
            CurrentSession.command.CommandText = SQLx;
            CurrentSession.dataadapter.SelectCommand = SessionActual.command;

            CurrentSession.datatable.Clear();
            CurrentSession.datatable.Reset();
            CurrentSession.dataadapter.Fill(SessionActual.datatable);

            CurrentSession.connection.Close();
        }
        catch (Exception ex)
        {
            try
            {
                CurrentSession.connection.Close();
            }
            catch { }
            try
            {
                ex.Data.Add("SQLx", SQLx);
                ServicioTecnico.EnviarMailError("Error closing connection", ex);
            }
            catch { }
            throw;
        }

        return CurrentSession.datatable.Copy();
    }
}

All of this worked fine ultil we needed to implement a time consuming process in a new thread... In the second thread HttpContext.Current.Session is null (we know its because the current context its different between threads) so everything fails :S

Investigating we found that you could pass the session from one thread to another like this:

 using App_Code;
 public partial class Example: Page
 {
    private void startoperation
    {
        Session["savedsession"] = HttpContext.Current.Session;
        ThreadStart operation = delegate { buscar(); };
        Thread thread = new Thread(operation);
        thread.Start();
    }
    private void longoperation
    {
        HttpSessionState mySession = ((HttpSessionState)Session["savedsession"]);
        //what we would like to do
        //CurrentSession.Session = mySession;

        Funx fun=new Funx();
        DataTable resul=Funx.QuerySQLDT(select * from exampletable");
    }
 }

what we would like to do is asociate the session to the new thread (CurrentSession.Session = mySession;) so every function works as is without changing them (there is a lot and we dont want to change all the structure of the application for this last addition) but HttpContext.Current.Session has no setter :S (we know we would have to add the setter to our CurrentSession.Session property)

So...how would you solve it? Any great tricks? One idea we had is to convert CurrentSession.Session as a dinamic pointer or something like that so when we are going to use the functions inside the second thread the getter of CurrentSession.Session would return the session from a temp variable passed for the case of the thread...but we dont have a clear idea how to implement it...a possible draft would be:

public static class CurrentSession
{
    public static HttpSessionState magicpointer;

    public static HttpSessionState Session
    {
        get
        {
            //return HttpContext.Current.Session;
            return magicpointer;
        }
        set
        {
            magicpointer=value;
        }
    }
}

public partial class Example : Page
{
    bool completedtask=false; //we know this would be Session variable or so to work with threads
    private void startoperation
    {
        Session["savedsession"] = HttpContext.Current.Session;
        ThreadStart operation = delegate { buscar(); };
        Thread thread = new Thread(operation);
        thread.Start();
    }
    private void longoperation
    {
        HttpSessionState mySession = ((HttpSessionState)Session["savedsession"]);

        CurrentSession.Session = mySession;
        //or set the magicpointer...whatever works...
        CurrentSession.magicpointer= mySession;

        Funx fun=new Funx();
        DataTable resul=Funx.QuerySQLDT(select * from exampletable");

        //time consuming work...

        completedtask=true; //change the flag so the page load checker knows it...
    }
    private void page_load_checker()
    { //this combined with javascript that makes the page postback every 5 seconds or so...
       if(completedtask)
       {
           //show results or something like that
           //set the CurrentSession.magicpointer or CurrentSession.Session 
           //to point the HttpContext.Current.Session again...
           CurrentSession.magicpointer=HttpContext.Current.Session;
       }
    }
}

So thats the history...sorry about making this post so long but we wanted to be clear about the situation to prevent confusions and deviated answers...thanks!

Answer

Yuliy picture Yuliy · Jan 31, 2011

You'd probably be better served by refactoring your code. Have your functions actually take parameters that they operate on, instead of relying on the data to be there ambiently (in the session). If you have a function that needs to know who the current user is, then tell it who the current user is.