Доступ к HttpSessionState (HttpContext.Current.Session) из другого потока или хитрости? - PullRequest
4 голосов
/ 25 ноября 2010

У нас есть веб-сайт, который реализует центральное управление HttpSessionState в App_Code следующим образом:

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;
    }
}

Класс функций, который его использует (например):

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();
    }
}

Все этосработало нормально, пока нам не потребовалось реализовать трудоемкий процесс в новом потоке ... Во втором потоке HttpContext.Current.Session имеет значение null (мы знаем его, потому что текущий контекст различен в разных потоках), поэтому все не получается: S

В ходе исследования мы обнаружили, что вы можете передавать сеанс из одного потока в другой, как это:

 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");
    }
 }

, что мы хотели бы сделать, это перенести сеанс в новый поток (CurrentSession.Session = mySession;) поэтому каждая функция работает как есть, не меняя их (их много, и мы не хотим менять всю структуру приложения для этого последнего добавления), но HttpContext.Current.Session не имеет установщика: S (мы знаем, что нам придетсядобавьте установщик в наше свойство CurrentSession.Session)

Итак ... как бы вы решили это?Какие-нибудь отличные трюки?У нас была идея преобразовать CurrentSession.Session в качестве динамического указателя или чего-то подобного, чтобы, когда мы собираемся использовать функции во втором потоке, получатель CurrentSession.Session возвращал сеанс из временной переменной, переданной для случаяпоток ... но у нас нет четкого представления о том, как его реализовать ... возможный черновик будет:

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;
       }
    }
}

Так вот история ... извините за то, что сделали этот пост так долго, но мыхотел прояснить ситуацию, чтобы избежать путаницы и отклонения ответов ... спасибо!

Ответы [ 2 ]

2 голосов
/ 01 февраля 2011

Вероятно, вам будет лучше обслужить рефакторинг вашего кода.Пусть ваши функции на самом деле принимают параметры, с которыми они работают, вместо того, чтобы полагаться на данные, присутствующие в окружении (в сеансе).Если у вас есть функция, которая должна знать, кто является текущим пользователем, то скажите ей, кто является текущим пользователем.

1 голос
/ 31 января 2011

Вы можете создать интерфейс.

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

Тогда у вас может быть два класса, которые его реализуют.

//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;
    }
}

Пусть ваш класс потока примет ISession в качестве параметра.Когда вы создаете или запускаете ваш поток, преобразуйте ProgramHttpSession в ProgramSession (конструктор должен покрыть это) и передайте объект ProgramSession в ваш поток.Таким образом, ваше приложение и поток будут работать с одним и тем же интерфейсом, но не с одной и той же реализацией.

Это должно не только решить вашу проблему, но и значительно упростить тестирование, поскольку ваш поток больше не зависит от HttpSessionState.Теперь при тестировании вашего потока вы можете передать любой класс, который реализует этот ISession интерфейс.

...