Me surgió la necesidad de poder cargar un Assembly en un AppDomain que no sea sobre el que se está ejecutando mi aplicación ya que al cargar en forma masiva una gran cantidad el consumo de memoria crecía “hasta el infinito y más allá”. El objetivo de cargar este Assembly en un AppDomain distinto al que corre mi aplicación es aprovechar la posibilidad de poder descargar el AppDomain ni bien se termina la ejecución del código del assembly liberando los recursos casi en forma instantánea.
De mas esta decir que no se disponen de estos assembly físicamente sino que llegan a mi componente como un array de bytes…
Este assembly se genera a partir de un código similar a este:
namespace OtherDomain
{
public class Messenger
{
public string GetMessage(string Name)
{
return string.Format("Hello {0} from {1}!)",
Name,
AppDomain.CurrentDomain.FriendlyName);
}
}
}
Primer Intento
AppDomain appDomain = AppDomain.CreateDomain("MyDomain");
Assembly assembly = appDomain.Load(rawAssembly);
Resulta que al ejecutar
appDomain.Load(rawAssembly)se dispara una excepción indicando que el archivo del assembly no pudo ser encontrado… Estimo que es por la regla del CLR que no permite que los objetos de un AppDomain sean accedidos en forma directa desde otro AppDomain.
Segundo IntentoLa solución llego de la mano del método “DoCallBack” de la clase AppDomain. Este método permite la comunicación entre dominios de aplicación diferentes a través de un delegado de tipo “CrossAppDomainDelegate”. La firma del método que se debe utilizar como delegado es muy sencilla: “void NombreMetodo()”.
En primer lugar se crea el AppDomain:
NewAppDomain = AppDomain.CreateDomain("NewDomain");
A continuación se establecen los valores que deben enviarse desde el contexto actual de ejecución al nuevo contexto:
NewAppDomain.SetData("RawAssembly", rawAssembly);
NewAppDomain.SetData("YourName", YourName);
Por último se llama al método DoCallBack:
NewAppDomain.DoCallBack(new CrossAppDomainDelegate(Loader));
El método “Loader”, en términos generales recupera los datos establecidos anteriormente, carga el assembly, crea una instancia la clase “Messenger” y ejecuta el método “GetMessage”:
private static void Loader()
{
byte[] rawAssembly = null;
Assembly assembly = null;
object Messenger = null;
Type type = null;
MethodInfo method = null;
object result = null;
string YourName = null;
try
{
//Get Data
rawAssembly = (byte[])AppDomain.CurrentDomain.GetData("RawAssembly");
YourName = (string)AppDomain.CurrentDomain.GetData("YourName");
//Load Assembly
assembly = AppDomain.CurrentDomain.Load(rawAssembly);
Messenger = assembly.CreateInstance(”OtherDomain.Messenger”);
type = assembly.GetType(”OtherDomain.Messenger”);
method = type.GetMethod(”GetMessage”);
result = method.Invoke(Messenger, new string[]{ YourName });
AppDomain.CurrentDomain.SetData("Result", result);
}
finally
{
rawAssembly = null;
assembly = null;
Messenger = null;
type = null;
method = null;
result = null;
YourName = null;
}
}
Una vez ejecutado el método “Loader”, se obtienen los resultados:
if (NewAppDomain.GetData("Result") != null &&
NewAppDomain.GetData("Result").GetType() == typeof(string))
{
return (string)NewAppDomain.GetData("Result");
}
else
{
return string.Empty;
}
El código fuente completo se puede descargar desde
aquí