2011-02-01 16 views
5

Tenemos un sistema de complemento donde el código del complemento se ejecuta en un dominio de aplicación separado del proceso principal, usando .NET remoto para que los objetos se comuniquen..NET Remoting y HttpContext.Current

Una clase es similar a HttpContext.Current (que también sufre del problema) (editar, la implementación real):

public class MyClass 
{ 
    public static MyClass Instance 
    { 
     get 
     { 
      if(HttpContext.Current != null) 
       return HttpContext.Current.Items["MyClassInstance"]; 
     } 
     set 
     { 
      if(HttpContext.Current != null) 
       HttpContext.Current.Items["MyClassInstance"] = value; 
     } 
    } 
} 

Entonces, tenemos un objeto comunicativo que hereda de MarshalByRefObject:

public class CommunicatingClass : MarshalByRefObject, ICommunicatingClass 
{ 
    public void DoSomething() 
    { 
     MyClass.Instance.DoSomething(); 
    } 
} 

CommunicatingClass se crea en el dominio de aplicación principal, y funciona bien. Luego, está la clase de complemento, que se crea en su dominio de aplicación, y se le dio una instancia de la CommunicatingClass:

public class PluginClass 
{ 
    public void DoSomething(ICommunicatingClass communicatingClass) 
    { 
     communicatingClass.DoSomething(); 
    } 
} 

El problema es que, a pesar de que CommunicatingClass reside en el dominio de aplicación principal (verificado con la ventana Inmediato), todos los datos estáticos como MyClass.Instance y HttpContext.Current han desaparecido y son nulos. Tengo la sensación de que MyClass.Instance se recupera de alguna manera del plugin AppDomain, pero no estoy seguro de cómo resolverlo.

Vi otra pregunta que sugería RemotingServices.Marshal, pero eso no pareció ayudar, o lo usé incorrectamente. ¿Hay alguna manera de que CommunicatingClass pueda acceder a todos los métodos y propiedades estáticos como cualquier otra clase en el dominio de aplicación principal?

Editar:

El PluginClass se da un ejemplo de esta manera:

public static PluginClass Create() 
{ 
    var appDomain = GetNewAppDomain(); 
    var instance = (PluginClass)appDomain.CreateInstanceAndUnwrap(assembly, type); 
    instance.Communicator = new CommunicatingClass(); 
    return instance; 
} 

Editar 2:

podría haber encontrado la fuente del problema. MyClass.Instance se almacena en HttpContext.Current.Items (ver edición anterior).

¿Hay alguna forma de que HttpContext.Current pueda acceder al HttpContext correcto? Todavía me pregunto por qué, aunque se está ejecutando en HttpContext.DownDomain de la versión actual, CommunicatingClass.DoSomething, cuando se llama a MyClass.Instance, recupera cosas del dominio de aplicación de PluginClass (si tiene sentido).

+0

En caso de que no lo sabía: Remoting se ha desaprobado en favor de WCF. –

+0

Podría ser útil incluir el código que utiliza para obtener una referencia a communicatingClass en el dominio de la aplicación PluginClass. – JohnOpincar

+0

@ John Saunders, ¿Cómo funciona WCF con la comunicación cross-appdomain? Tiene sentido cuando el cliente y el servidor están separados, pero no parece que sea tan fácil como los proxys remotos transparentes en este caso. Sin embargo, nunca he trabajado con WCF, solo lo miré. – Snea

Respuesta

9

Así que mi compañero de trabajo y yo resolvimos esto, finalmente, con un montón de ayuda de Reflector.

El problema principal es que HttpContext.Current es nulo cuando se accede a través de una llamada remota.

La propiedad HttpContext.Current se almacena en una casa solariega interesante. Unos cuantos incubadores anidados, llegas al CallContext.HostContext. Esta es una propiedad de objeto estático en CallContext.

Cuando se establece CallContext, primero comprueba si el valor es ILogicalThreadAffinitive.

  • Si lo es, almacena el valor en el LogicalCallContext del subproceso actual.
  • Si no lo está, almacena el valor en el número actual IllogicalCallContext.

HttpContext es no un ILogicalThreadAffinitive, por lo que se almacena en el IllogicalCallContext.


Luego, hay comunicación remota.

No profundizamos demasiado en su origen, pero lo que hace se deduce de algunas otras funciones.

Cuando se realiza una llamada a un objeto remoto de un dominio de aplicación diferente, la llamada no es aproximado directamente a la rosca original, que se ejecuta en el mismo contexto de ejecución exacta.

Primero, se captura el ExecutionContext del hilo original (el que contiene HttpContext.Current), a través de ExecutionContext.Capture (más en esto en un poco).

Entonces, el ExecutionContext de regresar de Capture se pasa como el primer argumento de ExecutionContext.Run, formando absoluto se refiere el código:

Delegate myRemoteCall; //Assigned somewhere else in remoting 
ExecutionContext.Run(ExecutionContext.Capture(), x => { myRemoteCall() }, null); 

Entonces, completamente transparente, se accede a su código en el objeto remoto.

Desafortunadamente, HttpContext.Current no se ha capturado en ExecutionContext.Capture().

Aquí radica la diferencia esencial entre IllogicalCallContext y LogicalCallContext.

Capture crea una nueva marca ExecutionContext, esencialmente copiando todos los miembros (como el LogicalCallContext) en el nuevo objeto. Pero, hace no copia el IllogicalCallContext.

lo tanto, ya no es una HttpContextILogicalThreadAffinative, que no puede ser capturado por ExecutionContext.Capture.


¿La solución?

HttpContext no es un MarshalByRefObject o [Serializable] (probablemente por una buena razón), por lo que no se puede pasar al nuevo AppDomain.

Pero, puede cruzar ExecutionContext s sin problema.

Por lo tanto, en el dominio de aplicación principal MarshalByRefObject que se proporciona como un proxy para el otro AppDomain, en el constructor se le da la instancia de HttpContext.Current.

Luego, en cada llamada al método del nuevo objeto (por desgracia), ejecute:

private HttpContext _context; 
private void SyncContext() 
{ 
    if(HttpContext.Current == null) 
     HttpContext.Current = _context; 
} 

Y se establecerá sin problema. Desde HttpContext.La corriente está vinculada al IllogicalCallContext del ExecutionContext, no sangrará a ningún otro subproceso que pueda crear ASP.NET, y se limpiará cuando se elimine la copia ExecutionContext.

(puedo estar equivocado sobre gran parte de esto, aunque es toda la especulación y la reflexión.)

+0

Pensé que tu problema era con MyClass, no con HttpContect.Current. Quizás debas modificar tu pregunta. ¿O está diciendo en esta respuesta que MyClass todavía no es accesible? – JohnOpincar

+0

MyClass se almacenó en HttpContext, descubrí. Cambiaré el título. – Snea

1

Creo que necesitarás derivar MyClass de MarshalByRefObject también.

+0

Sí, todas las clases que dependen de una referencia específica a un objeto remoto (es decir, un singleton en este caso) necesitan extenderse desde 'MarshalByRefObject' también, de lo contrario se pierden en el otro lado del dominio de la aplicación. –

+0

Extraño, lo había intentado antes, y todavía tengo el mismo problema. Voy a jugar un poco con eso un poco más. Además, ¿por qué sería esto? Si la instancia de CommunicatingClass está en el mismo AppDomain que MyClass, ¿no estaría visible allí? ¿O está CommunicatingClass en algún lugar "entre" dominios? – Snea

+0

Véase la edición anterior. MyClass siendo un MarshalByRefObject probablemente arregla su propio caso, pero HttpContext.Current no puede ser un MarshalByRefObject. – Snea

Cuestiones relacionadas