从另一个线程或技巧访问HttpSessionState(HttpContext.Current.Session)?

时间:2010-11-25 09:46:46

标签: c# asp.net multithreading session httpsession

我们有一个网站,它在App_Code中实现了一个中央HttpSessionState管理,如下所示:

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没有setter:S(我们知道我们必须将setter添加到CurrentSession.Session属性中)

那么......你怎么解决它?有什么好招数吗? 我们有一个想法是将CurrentSession.Session转换为dinamic指针或类似的东西,所以当我们要使用第二个线程内的函数时,CurrentSession.Session的getter将从为该情况传递的临时变量返回会话线程......但我们并不清楚如何实现它......可能的草案将是:

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 个答案:

答案 0 :(得分:2)

重构代码可能会更好。让您的函数实际上采用它们操作的参数,而不是依赖于数据在环境中(在会话中)。如果您有一个需要知道当前用户是谁的功能,那么告诉它当前用户是谁。

答案 1 :(得分:1)

您可以创建一个界面。

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接口的类。