2010-11-25 34 views
4

Nous avons un site Web qui met en œuvre une gestion HttpSessionState centrale dans App_Code comme ceci:Accéder à HttpSessionState (HttpContext.Current.Session) à partir d'un autre thread ou d'autres astuces?

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

Fonctions classe qui l'utilise (par exemple):

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

Tout cela a bien fonctionné ultil nous avions besoin pour implémenter un processus chronophage dans un nouveau thread ... Dans le deuxième thread HttpContext.Current.Session est null (nous savons que c'est parce que le contexte actuel est différent entre threads) donc tout échoue: S

enquête nous avons constaté que vous pourriez passer la session d'un fil à un autre comme celui-ci:

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

ce que nous voudrions faire est Asociate la session au nouveau thread (CurrentSession.Session = MaSession;) de sorte que chaque function fonctionne tel quel sans les changer (il y en a beaucoup et nous ne voulons pas changer toute la structure de l'application pour cette dernière addition) mais HttpContext.Current.Session n'a pas de setter: S (nous savons que nous devrions ajouter le setter à notre propriété CurrentSession.Session)

Alors ... comment le résoudre? Des trucs géniaux? Une idée que nous avions était de convertir CurrentSession.Session comme un pointeur dinamic ou quelque chose comme ça alors quand nous allons utiliser les fonctions à l'intérieur du deuxième thread le getter de CurrentSession.Session retournerait la session à partir d'une variable temp passée pour le cas du fil ... mais nous ne disposons pas une idée claire comment la mettre en œuvre ... un projet possible serait:

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

donc c'est l'histoire ... désolé de faire ce poste si longtemps, mais nous voulions être clair sur la situation pour éviter les confusions et les réponses déviées ... merci!

+0

L'idée est d'avoir un ensemble central de fonctions qui (nous avons fait plus tard une bibliothèque de fonctions pour l'utiliser facilement dans n'importe quel projet web) traitera des requêtes de bases de données, variables utilisateur courantes etc ... sans avoir à passer chaque variable de méthode en méthode, de page en page, de thread en thread ... – VSP

+0

Nous sommes ouverts à changer toute la structure, mais l'objectif rend aussi facile d'utiliser ces fonctions que maintenant, pas pour le pire. – VSP

Répondre

1

Vous pouvez créer une interface.

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

Ensuite, vous pouvez avoir deux classes qui l'implémentent.

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

Demandez à votre classe de fil prendre un ISession comme paramètre. Lorsque vous créez ou démarrez votre thread, convertissez le ProgramHttpSession en un ProgramSession (le constructeur doit le couvrir) et passez l'objet ProgramSession à votre discussion. De cette façon, votre application et votre thread fonctionneront sur la même interface mais pas sur la même implémentation.

Cela devrait non seulement résoudre votre problème, mais aussi faciliter les tests puisque votre thread ne dépend plus d'un HttpSessionState. Maintenant, lorsque vous testez votre thread, vous pouvez passer dans n'importe quelle classe qui implémente cette interface ISession.

+0

Cela fonctionnerait mais nécessiterait encore de refactoriser toutes les méthodes/classes en question pour travailler avec un 'ISession' – Basic

+0

Le problème est que dans notre cas il y a beaucoup de méthodes et de variables ... et de les passer tous manuellement dans ceux cas + rappel d'ajouter chaque nouvelle variable que nous mettons dans la session ...: S – VSP

2

Vous seriez probablement mieux servi en refactorisant votre code. Vos fonctions prennent en fait les paramètres sur lesquels elles opèrent, au lieu de s'appuyer sur les données pour être là ambiant (dans la session). Si vous avez une fonction qui a besoin de savoir qui est l'utilisateur actuel, dites-lui qui est l'utilisateur actuel.

+2

+1 Cela sera très douloureux à court terme et extrêmement bénéfique à long terme. Il simplifie les tests unitaires, l'IoC, l'injection de dépendances, etc. - Si cela est même possible à distance, c'est une bonne idée. – Basic