2010-05-28 10 views
7

que han estado tratando de obtener el código siguiente para trabajar (todo lo que se define en el mismo conjunto):¿Cómo paso referencias como parámetros de método en AppDomains?

namespace SomeApp{ 

public class A : MarshalByRefObject 
{ 
    public byte[] GetSomeData() { // } 
} 

public class B : MarshalByRefObject 
{ 
    private A remoteObj; 

    public void SetA(A remoteObj) 
    { 
     this.remoteObj = remoteObj; 
    } 
} 

public class C 
{ 
    A someA = new A(); 
    public void Init() 
    { 
     AppDomain domain = AppDomain.CreateDomain("ChildDomain"); 
     string currentAssemblyPath = Assembly.GetExecutingAssembly().Location; 
     B remoteB = domain.domain.CreateInstanceFromAndUnwrap(currentAssemblyPath,"SomeApp.B") as B; 
     remoteB.SetA(someA); // this throws an ArgumentException "Object type cannot be converted to target type." 
    } 
} 

} 

Lo que estoy tratando de hacer es pasar una referencia de una 'A' instancia creada en el primer AppDomain para el dominio secundario y hacer que el dominio secundario ejecute un método en el primer dominio. En algún punto del código 'B', voy a llamar a 'remoteObj.GetSomeData()'. Esto debe hacerse porque el método 'byte []' de 'GetSomeData' debe 'calcularse' en el primer dominio de aplicación. ¿Qué debo hacer para evitar la excepción o qué puedo hacer para lograr el mismo resultado?

+0

Tu código funciona para mí. –

+0

+1 Para mí también. ¿Qué versiones de CLR, Visual Studio (si hay), C#, etc.? ¿Alguna otra circunstancia? –

+0

extraño, voy a volver a comprobar –

Respuesta

2

Puedo duplicar el problema, y ​​parece estar relacionado con TestDriven.net y/o xUnit.net. Si ejecuto C.Init() como método de prueba, recibo el mismo mensaje de error. Sin embargo, si ejecuto C.Init() desde una aplicación de consola, no obtengo la excepción.

¿Está viendo lo mismo, ejecutando C.Init() de una prueba de unidad?

Editar: También puedo duplicar el problema usando NUnit y TestDriven.net. También puedo duplicar el error usando el corredor NUnit en lugar de TestDriven.net. Así que el problema parece estar relacionado con la ejecución de este código a través de un marco de prueba, aunque no estoy seguro de por qué.

+0

No estaba en un marco de prueba, pero me ayudó a entender lo que estaba causando, gracias. –

+0

Genial, ¿qué estaba causando? –

+0

No estoy exactamente seguro, pero tiene algo que ver con la ruta de inicio de la aplicación –

10

La causa real fue que tu DLL se cargaba desde diferentes ubicaciones en los dos dominios de aplicación diferentes. Esto hace que .NET crea que son ensamblajes diferentes, lo que significa que los tipos son diferentes (aunque tengan el mismo nombre de clase, espacio de nombres, etc.).

La razón por la cual la prueba de Jeff falló cuando se ejecutó a través de un marco de prueba de unidad es porque los marcos de prueba de unidad generalmente crean AppDomains con ShadowCopy establecido en "verdadero". Pero su AppDomain creado de manera manual tendría como valor predeterminado ShadowCopy = "false". Esto provocaría que los dlls se cargaran desde diferentes ubicaciones, lo que lleva a la agradable "Tipo de objeto no se puede convertir a tipo de destino". error.

ACTUALIZACIÓN: después de más pruebas, parece ser que ApplicationBase es diferente entre los dos AppDomains. Si coinciden, entonces el escenario anterior funciona. Si son diferentes, no (aunque he confirmado que el dll se carga en ambos AppDomains desde el mismo directorio usando windbg) Además, si activo ShadowCopy = "true" en mis AppDomains, entonces falla con un mensaje diferente: "System.InvalidCastException: Object debe implementar IConvertible".

UPDATE2: Lectura adicional me lleva a creer que está relacionado con Load Contexts. Cuando utiliza uno de los métodos "De" (Assembly.LoadFrom, o appDomain.CreateInstanceFromAndUnwrap), si el ensamblado se encuentra en una de las rutas de carga normales (la ApplicationBase o una de las rutas de exploración), entonces se carga en el valor predeterminado. Cargar contexto. Si el ensamblaje no se encuentra allí, entonces se carga en el Contexto Load-From. Entonces, cuando ambos AppDomains coinciden con ApplicationBase, aunque usemos el método "De", ambos se cargan en el contexto de carga predeterminado de su Dominio de aplicación. Pero cuando las ApplicationBase son diferentes, entonces un AppDomain tendrá el ensamblado en su contexto de carga predeterminado mientras que el otro tiene el ensamblado en su contexto de carga desde.

+0

pero incluso después de ** 'setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;' ** el problema todavía se presenta ... – Shelest

+0

@Shelest Trate de configurar su ApplicationBase en Path.GetDirectoryName ("ruta del archivo dll") – Anshul

+0

@RussellMcClure : Gracias por su información, me ayudó a resolver esto por mí. Puede echar un vistazo a mi respuesta y darme su opinión sobre mi solución, ya que no es realmente elegante. IMO ... – ChrFin

0

Este es un comentario a @RussellMcClure pero como es a lo complejo de un comentario he puesto esto como una respuesta:

estoy dentro de un ASP.NET y apagar copia sombra (que también resolvería el problema) no es realmente una opción, pero he encontrado la siguiente solución:

AppDomainSetup adSetup = new AppDomainSetup(); 
if (AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles == "true") 
{ 
    var shadowCopyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 
    if (shadowCopyDir.Contains("assembly")) 
     shadowCopyDir = shadowCopyDir.Substring(0, shadowCopyDir.LastIndexOf("assembly")); 

    var privatePaths = new List<string>(); 
    foreach (var dll in Directory.GetFiles(AppDomain.CurrentDomain.SetupInformation.PrivateBinPath, "*.dll")) 
    { 
     var shadowPath = Directory.GetFiles(shadowCopyDir, Path.GetFileName(dll), SearchOption.AllDirectories).FirstOrDefault(); 
     if (!String.IsNullOrWhiteSpace(shadowPath)) 
      privatePaths.Add(Path.GetDirectoryName(shadowPath)); 
    } 

    adSetup.ApplicationBase = shadowCopyDir; 
    adSetup.PrivateBinPath = String.Join(";", privatePaths); 
} 
else 
{ 
    adSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; 
    adSetup.PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath; 
} 

Esto utilizará el directorio de copia sombra de la principal aplicación en el dominio como la base de la aplicación y agregue todos los ensamblados copiados en la sombra a la ruta privada si la copia oculta está habilitada.

Si alguien tiene una mejor manera de hacerlo, dígame por favor.

Cuestiones relacionadas