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

18,314

Solution 1

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.

Solution 2

You could create an interface.

public interface ISession
{
    public ControlUsu user {get; set;}
    public OdbcConnection connection {get; set;}
    //Other properties and methods...
}

Then you can have two classes that implement it.

//Use this class when you have HttpSessionState
public class ProgramHttpSession : ISession
{
    public ControlUsu user
    {
        get {return (ControlUsu)Session["currentuser"];}
        set {Session["currentuser"] = value;}
    }
    public OdbcConnection connection
    {
        get {return (OdbcConnection)Session["currentconnection"];}
        set {Session["currentconnection"] = value;}
    }
}

//Use this class when you DON'T have HttpSessionState (like in threads)
public class ProgramSession : ISession
{
    private ControlUsu theUser;
    public ControlUsu user
    {
        get {return theUser;}
        set {theUser = value;}
    }

    private OdbcConnection theConnection;
    public OdbcConnection connection
    {
        get {return theConnection;}
        set {theConnection = value;}
    }

    public ProgramSession(ControlUsu aUser, OdbcConnection aConnection)
    {
        theUser = aUser;
        theConnection = aConnection;
    }
}

Have your thread class take an ISession as a parameter. When you create or start your thread convert the ProgramHttpSession into a ProgramSession (the constructor should cover this) and pass the ProgramSession object to your thread. This way your application and thread will be working against the same interface but not the same implementation.

This should not only solve your problem but make testing much easier since your thread no longer depends on an HttpSessionState. Now when testing your thread you can pass in any class that implements that ISession interface.

Share:
18,314
VSP
Author by

VSP

Updated on June 04, 2022

Comments

  • VSP
    VSP almost 2 years

    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!