Acessar dados da session de outro segmento

Eu tenho um problema aqui. No meu aplicativo da web, eu tenho uma página que inicia outro thread para tarefa demorada. Neste novo tópico eu tenho uma chamada para um dos meus methods de arquitetura (dentro de outro projeto – um projeto de arquitetura) . O problema é: em um desses methods eu access um campo HttpContext.Current.Session . Mas quando eu lanço o aplicativo, uma exceção é lançada dizendo que esse object ( HttpContext.Current.Session ) tem uma referência nula. Como eu poderia definir o contexto do novo segmento da mesma forma que o contexto HttpApplication para acessar o HttpContext.Current.Session ?

Há várias coisas a considerar aqui.

Se o segmento tiver uma vida útil igual à da página e você precisar de um bom access random ao HttpSessionState , deverá obter o SynchronizationContext da chamada que cria o thread em segundo plano usando a propriedade Current estática.

Depois de ter isso, você pode passar isso para o seu thread e, em seguida, quando precisar de access a qualquer coisa no HttpContextBase associado à solicitação (e isso inclui a session), você poderá chamar o método Post no SynchronizationContext que você transmitiu ao seu thread para obter valores (ou configurá-los):

 // From thread servicing request. var sc = SynchronizationContext.Current; // Run the task Task t = Task.Run(() => { // Do other stuff. // ... // The value to get from the session. string sessionValue = null; // Need to get something from the session? sc.Post(() => { // Get the value. sessionValue = HttpContext.Current.Session["sessionValue"]; } // Do other stuff. // ... }); 

É importante fazer isso, já que o access ao HttpContextBase (e qualquer coisa associada a ele) não é thread-safe e está vinculado ao thread (bem, o contexto) processando a solicitação.

Observe que o método Post não bloqueia, portanto, o código que vem após a chamada para Post (ou seja, as linhas depois de // Do other stuff. ) Deve ser independente do delegado que foi passado para Post . Se o código que vier depois for dependente e você precisar aguardar a conclusão da chamada antes de continuar, você poderá chamar o método Send ; ele tem a mesma assinatura e bloqueará até que o código no delegado seja executado.

Dito isso, se você quiser apenas access somente leitura aos valores, é melhor obtê-los antes de chamar seu código e, em seguida, acessá-los no thread de segundo plano:

 // Get the values needed in the background thread here. var values = { SessionValue = HttpContext.Current.Session["sessionValue"]; }; // Run the task Task t = Task.Run(() => { // Do other stuff. // ... // Work with the session value. if (values.SessionValue == ...) // Do other stuff. // ... }); 

Se o thread continuar a processar após a manutenção da solicitação, você terá apenas o estado somente leitura e terá que capturá-lo antes de iniciar o thread. Uma vez que a solicitação é atendida, mesmo que a session viva, é um conceito lógico; dependendo do provedor para o estado da session (gerenciador de estado da session, SQL Server, etc.), o object pode ser hidratado sempre que uma nova solicitação chegar.

Você também teria que lidar com problemas de tempo limite de session, você não saberia se a session existe no ponto em que deseja acessá-la.

Você não pode acessar a session de dentro de um segmento, mas você pode compartilhar seus dados usando: HttpRuntime.Cache

Há algumas coisas que você deve ter em mente: ao contrário da session, o cache expira. Além disso, o cache é compartilhado entre todos os usuários da web.

Se você passar o contexto atual para o segmento filho, o problema é que ele depende do contexto pai. Se o contexto pai morrer, o seu segmento não terá mais access ao contexto e causará problemas.

Uma solução é clonar o contexto pai e, em seguida, usar o clone no encadeamento. Dessa forma, se o thread pai for descartado, o thread continuará funcionando e terá access a todos os resources do contexto.

 HttpContext ctx = ThreadingFixHttpContext(); Thread newThread = new System.Threading.Thread(new ThreadStart(() => { HttpContext.Current = ctx; Thread.CurrentPrincipal = ctx.User; var test = HttpContext.Current.Session["testKey"]; })); newThread.Start(); 

Isso também deve funcionar com o Task.Run () também. Os methods que fazem o trabalho:

  private static Object CloneObject(Object Source) { MemoryStream Stream = new MemoryStream(); System.Runtime.Serialization.Formatters.Binary.BinaryFormatter Formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); Formatter.Serialize(Stream, Source); Stream.Position = 0; object Clone = (object)Formatter.Deserialize(Stream); Stream.Close(); Stream.Dispose(); return Clone; } public static System.Web.HttpContext ThreadingFixHttpContext() { //If this method is called from a new thread there is issues holding httpContext.current (which is injected from parent thread in AniReturnedPaymentsFetch.ascx.cs //The parent http current will die of its own accord (because it is from a different thread) //So we clone it into thread current principal. System.Security.Principal.WindowsIdentity ThreadIdentity = (System.Security.Principal.WindowsIdentity)CloneObject(System.Web.HttpContext.Current.User.Identity); //Then create a new httpcontext using the parent's request & response, so now the http current belongs to this thread and will not die. var request = System.Web.HttpContext.Current.Request; var response = System.Web.HttpContext.Current.Response; var ctx = new System.Web.HttpContext(request, response); ctx.User = new System.Security.Principal.WindowsPrincipal(ThreadIdentity); return ctx; }