2009-09-16 25 views
7

Creo que necesito ayuda para entender cómo persisten los objetos estáticos en una aplicación ASP.Net. Tengo este escenario:¿Por qué mi función estática de ASP.Net se cruza "contextualmente" entre sesiones de usuario?

someFile.cs en una biblioteca de clases:

public delegate void CustomFunction(); 

public static class A { 
    public static CustomFunction Func = null; 
} 

someOtherFile.cs en una biblioteca de clases:

public class Q { 
    public Q() { 
     if (A.Func != null) { 
      A.Func(); 
     } 
    } 
} 

Algunos página ASP.Net:

Page_Init { 
    A.Func = MyFunc; 
} 

public void MyFunc() { 
    System.IO.File.AppendAllText(
     "mydebug.txt", DateTime.Now.ToString("hh/mm/ss.fff", Session.SessionID)); 
} 

Page_Load { 
    Q myQ = new Q(); 
    System.Threading.Thread.Sleep(20000); 
    mQ = new Q(); 
} 

La idea es que tengo un objeto de negocio que realiza alguna operación basada en una función de devolución de llamada en el nivel de UI. Establecí la función de devolución de llamada en una variable estática en Page_Init (en la versión de código real, en la página Maestra, si eso hace la diferencia). Pensé que cada ejecución de la página, sin importar de qué sesión de usuario provenía, pasaría por la lógica de esa función, pero operaría en su propio conjunto de datos. Lo que parece estar sucediendo en su lugar es un problema de concurrencia.

Si ejecuto una sesión de usuario, mientras duerme entre llamadas a esa función de devolución de llamada, inicie otra sesión de usuario, cuando la primera sesión vuelve de dormir, recoge la ID de sesión de la segunda sesión de usuario. como puede ser esto posible?

salida de mydebug.txt:

01/01/01.000 abababababab (session #1, first call) 
01/01/05.000 cdcdcdcdcdcd (session #2, first call - started 5 seconds after session #1) 
01/01/21.000 cdcdcdcdcdcd (session #1 returns after the wait but has assumed the function context from session #2!!!!!) 
01/01/25.000 cdcdcdcdcdcd (session #2 returns with its own context) 

¿Por qué es el contexto de la función (es decir, sus datos locales, etc.) se sobrescribe de una sesión de usuario a otro?

+0

Gracias, a todos, por la ayuda. Reglas de StackOverflow :) –

Respuesta

2

Una solución que puede considerar es el uso de [ThreadStatic].

http://msdn.microsoft.com/en-us/library/system.threadstaticattribute(VS.71).aspx

que hará que su estática por hilo. Sin embargo hay cavaets así que debes probar.

+0

Además de la nota de MSDN en su enlace sobre los valores iniciales, ¿cuáles son las advertencias? –

+0

Esto no funcionará en ASP.NET. No puede predecir qué hilo atenderá una solicitud determinada. – Brannon

+0

@Brannon, pero ¿no estamos seguros de que un solo hilo atenderá una sola solicitud? Creo que ese es mi problema: que myFunc de un hilo cambió en el medio de la operación por la asignación de otro hilo de myFunc. –

11

Cada solicitud a un sitio asp.net entra y se procesa en su propio hilo. Pero cada uno de esos hilos pertenece a la misma aplicación. Eso significa que cualquier cosa que marque como estática se comparte en todas las solicitudes y, por lo tanto, también en todas las sesiones y usuarios.

En este caso, la función MyFunc que es parte de su clase de página se copia sobre la parte superior de la Func miembro estático en A con cada Page_Init, y por lo que cada vez que un usuario hace una Page_Init, es reemplazando la A.Func utilizado por todas las solicitudes.

+0

Entiendo que la dirección de la función esté siendo reemplazada. No entiendo, sin embargo, por qué los datos a los que se accede en la función también se están cruzando. ¿Por qué la sesión # 1, que tiene Session.SessionID = abababababab, al volver a invocar myFunc, de repente tiene Session.SessionID = cdcdcdcdcdcd? –

+0

Porque el delegado captura todo el entorno de ese método, incluido el objeto de sesión actual cuando lo asigna al delegado estático. – nos

+0

Porque antes de que la sesión # 1 llame al método estático A.Func() en la sesión del constructor Q(), la sesión 2 reemplazó esa función con su propia implementación. Estás llamando a una función completamente diferente en la segunda vuelta. –

4

Los datos estáticos se comparten entre toda la application domain de su aplicación web. En resumen, se comparte entre todos los subprocesos que atienden solicitudes en su aplicación web, no está vinculado a una sesión/subproceso/usuario de ninguna manera, sino a toda la aplicación web (a diferencia de, por ejemplo, php donde cada solicitud vive en su propio entorno aislado bar algunos mandos proporcionados - como la variable de sesión.)

+0

Al igual que estoy tratando de verbalizar correctamente en mi comentario a Joel, entiendo por qué la referencia de función se comparte entre el dominio de la aplicación. No entiendo por qué las variables que SON de la sesión/thread/usuario vinculadas dentro de la lógica de la función también se están compartiendo entre el dominio de la aplicación. –

4

No intentaré mejorar las explicaciones de las respuestas de los miembros estáticos, pero quiero señalar otra forma de codificar su problema inmediato.

Como solución, se podría hacer una versión orientada a instancia de la clase A, almacenarlo en una variable de nivel de página, y pasarlo a Q 's constructor en la página de carga:

public class MyPage: Page { 
    private A2 _a2; 

    // I've modified A2's constructor here to accept the function 
    protected Page_Init() { this._a2 = new A2(MyFunc); } 

    protected Page_Load() { 
     Q myQ = new Q(this._a2); 
     // etc.. 
    } 
} 

En De hecho, si no hay una necesidad apremiante de declarar A2 anteriormente, podría crear una instancia al crear su instancia de Q en Page_Load.

Editar: Para responder a la pregunta planteada en otros comentarios, la razón por la que se comparten las variables es que las solicitudes comparten el mismo delegado, que tiene una sola copia de sus variables. Ver Jon Skeet's The Beauty of Closures para más detalles.

+0

DOH !!!!!!! Ahora todo tiene sentido para mí. Ni siquiera me di cuenta de que había establecido un cierre en este caso. Mi código de ejemplo es una versión extremadamente simplificada del código real; desafortunadamente, la solución propuesta no se ajusta al caso real. Pero muchas gracias por la explicación del cierre. –

Cuestiones relacionadas