¿Cómo paso referencias como parámetros de método a través de AppDomains?

He estado tratando de que el siguiente código funcione (todo está definido en el mismo ensamblaje):

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 instancia ‘A’ creada en el primer dominio de aplicación al 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 ‘remoteObj.GetSomeData ()’. Esto se debe hacer porque el ‘byte []’ del método ‘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?

Puedo duplicar el problema y parece estar relacionado con TestDriven.net y / o xUnit.net. Si ejecuto C.Init () como método de prueba, aparece 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 () desde una prueba de unidad?

Edición: también puedo duplicar el problema utilizando NUnit y TestDriven.net. También puedo duplicar el error usando el corredor NUnit en lugar de TestDriven.net. Entonces, 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é.

La causa raíz real era que su dll se estaba cargando desde diferentes ubicaciones en los dos dominios de aplicación diferentes. Esto hace que .NET piense que son ensamblajes diferentes, lo que por supuesto significa que los tipos son diferentes (aunque tengan el mismo nombre de clase, espacio de nombres, etc.).

La razón por la que 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 dominio de aplicación creado manualmente se establecería de forma predeterminada en ShadowCopy = “false”. Esto provocaría que las dll se carguen desde diferentes ubicaciones, lo que conduce a la buena “El tipo de objeto no se puede convertir al tipo de destino”. error.

ACTUALIZACIÓN: Después de más pruebas, parece que todo se reduce a que ApplicationBase es diferente entre los dos AppDomains. Si coinciden, entonces el escenario anterior funciona. Si son diferentes, no lo hace (aunque he confirmado que la dll está cargada en ambos AppDomains desde el mismo directorio usando windbg) Además, si enciendo ShadowCopy = “true” en mis dos AppDomains, entonces falla con un mensaje diferente: “System.InvalidCastException: el objeto debe implementar IConvertible”.

ACTUALIZACIÓN2: Lecturas adicionales me llevan a creer que está relacionado con los contextos de carga . Cuando utiliza uno de los métodos “De” (Assembly.LoadFrom, o appDomain.CreateInstanceFromAndUnwrap), si el ensamblaje se encuentra en una de las rutas de carga normales (ApplicationBase o una de las rutas de sondeo), entonces se carga en el valor predeterminado Contexto de carga. Si el ensamblaje no se encuentra allí, entonces se carga en el contexto de carga desde. Entonces, cuando ambos AppDomains coinciden con ApplicationBase, entonces aunque usamos un método “De”, ambos se cargan en el contexto de carga predeterminado de su respectivo AppDomain. Pero cuando los ApplicationBase son diferentes, entonces un dominio de aplicación tendrá el ensamblaje en su contexto de carga predeterminado, mientras que el otro tendrá el ensamblaje en su contexto de carga desde.

Este es un comentario para @RussellMcClure, pero como es complejo para un comentario, publico esto como respuesta:

Estoy dentro de una aplicación ASP.NET y desactivar la instantánea (que también solucionaría el problema) no es realmente una opción, pero encontré 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(); 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 usará el directorio de instantáneas del dominio principal de la aplicación como la base de la aplicación y agregará todos los ensamblajes copiados a la ruta privada si la instantánea está habilitada.

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