En tiempo de ejecución, me gustaría poder descargar una DLL y volver a cargar una versión modificada de la misma. Mi primer experimento se incendió. puede alguien decirme por que?El dominio de mi aplicación no se descargará
private static void Main()
{
const string fullPath = "C:\\Projects\\AppDomains\\distrib\\MyLibrary.dll";
// Starting out with a version of MyLibrary.dll which only has 1 method, named Foo()
AssemblyName assemblyName = AssemblyName.GetAssemblyName(fullPath);
AppDomain appDomain = AppDomain.CreateDomain("MyTemp");
appDomain.Load(assemblyName);
appDomain.DomainUnload += appDomain_DomainUnload;
AppDomain.Unload(appDomain);
// Breakpoint here; swap out different version of MyLibrary.dll which only has 1 method, named Goo()
AssemblyName assemblyName2 = AssemblyName.GetAssemblyName(fullPath);
AppDomain appDomain2 = AppDomain.CreateDomain("MyTemp2");
Assembly asm2 = appDomain2.Load(assemblyName2);
foreach (Type type in asm2.GetExportedTypes())
{
foreach (MemberInfo memberInfo in type.GetMembers())
{
string name = memberInfo.Name;
// Breakpoint here: Found Foo and but no Goo! I was expecting Goo and no Foo.
}
}
}
private static void appDomain_DomainUnload(object sender, EventArgs e)
{
// This gets called before the first breakpoint
}
Editar:
Vale, esto es, obviamente, mi primera publicación tiempo. Gracias a Daniel por formatear mi código (ahora veo el botón de la barra de herramientas para hacer eso, ¡y también el panel de vista previa!). No veo una manera de publicar un "comentario" en respuesta a los que pidieron una aclaración a la publicación original oa la respuesta 1, así que solo publicaré otra "respuesta" para continuar la conversación. (Se agradecerán sugerencias sobre cómo se hacen los comentarios).
Mitch - Cayó en llamas porque mi ciclo foreach debería haber estado iterando sobre los tipos en la DLL modificada, no en la previamente cargada/descargada. Matthew - Eso podría funcionar, pero realmente necesito el caso del mismo nombre de archivo para trabajar. Mike Two - No tiene un nombre muy fuerte.
Comentarios a la respuesta: Azul & Mike Two - Falsificaré en sus sugerencias, pero primero necesito entender un aspecto crítico. Leí que debe tener cuidado de no arrastrar el ensamblaje al dominio principal de la aplicación, y el código tenía previamente una copia del ciclo foreach antes de la descarga. Entonces, al sospechar que el acceso a MethodInfos estaba absorbiendo el ensamblado en el dominio principal de la aplicación, eliminé el ciclo. Fue entonces cuando supe que necesitaba pedir ayuda, ¡porque la primera DLL todavía no se descargaría!
Así que mi pregunta es: ¿Qué en el siguiente segmento de código hace que el dominio principal aplicación para siempre directamente (o indirectamente) el acceso nada en la DLL ... ¿por qué sería hacer que el conjunto para cargar también en la aplicación principal dominio:
appDomain.Load(assemblyName);
appDomain.DomainUnload += appDomain_DomainUnload;
AppDomain.Unload(appDomain);
no me dio mucha confianza que jamás había realmente ser capaz de utilizar la DLL antes de descargarlo.
Edición 2:
Mike Dos - Gracias por su persistencia ... cargar el ensamblado desde dentro DoCallBack era el ingrediente secreto. Para cualquier persona interesada, aquí hay más información que podría ser útil en el camino:
Parece que nadie podría reproducir mis condiciones exactamente. Para demostrar el problema original, generé mi dlls de esta manera: 1. Agregué el proyecto de la biblioteca de clase a la solución 2. Construí la versión 1.0.0 con Foo; el ensamblado resultante renombrado como MyLibrary.dll.f. 3. Renombrado Foo to Goo y construido otra versión 1.0.0; el ensamblaje resultante renombrado como MyLibrary.dll.g. 4. Se eliminó el proyecto de la solución. Antes de comenzar la ejecución, eliminé el .f y corrí al punto de interrupción (primera línea después de la descarga). Luego volví a conectar el .f y quité el .g del otro dll y corrí al siguiente punto de interrupción. Windows no me impidió cambiar el nombre. Nota: Aunque sería una mejor práctica, no cambié el número de versión porque no quería suponer que mis clientes siempre lo harían, ya que la entrada predeterminada en AssemblyInfo no es la versión de comodín. Parecía el caso más feo de manejar.
Además, acabo ahora descubrí algo que me habría puesta al tanto en cuanto antes:
AssemblyName assemblyName = AssemblyName.GetAssemblyName(FullPath);
AppDomain appDomain = AppDomain.CreateDomain("MyTemp");
appDomain.Load(assemblyName);
Assembly[] tempAssemblies = appDomain.GetAssemblies();
// MyLibrary.dll has been loaded into the temp domain...good
Assembly[] mainAssemblies = AppDomain.CurrentDomain.GetAssemblies();
// MyLibrary.dll has been loaded into the main domain, too...bad!
Así que no estoy seguro de lo que el punto de AppDomain.Load es, pero parece tener el efecto secundario de bonos de cargar el ensamblaje en el dominio principal de la aplicación también. El uso de este experimento, pude ver la solución de Mike dos limpiamente lo carga solamente en el dominio temporal:
AppDomain appDomain = AppDomain.CreateDomain("MyTemp");
appDomain.DoCallBack(CallBackDelegate); // Executes Assembly.LoadFrom
Assembly[] tempAssemblies = appDomain.GetAssemblies();
// MyLibrary.dll has been loaded into the temp domain...good
Assembly[] mainAssemblies = AppDomain.CurrentDomain.GetAssemblies();
// MyLibrary.dll has NOT been loaded into the main domain...great!
Así, Mike Dos, exactamente, ¿cómo marcar este novato Stackoverflow su respuesta como "aceptada"? ¿O no puedo hacer eso ya que solo soy un invitado aquí?
Ahora me voy a aprender cómo realmente usar MyLibrary sin aspirar el ensamblado al dominio principal de la aplicación. Gracias a todos por participar.
Datos 3:
BlueMonkMN - Me siguió adelante y se llevó a cabo la suscripción de eventos y obtener el mismo resultado. El programa completo está ahora en la lista a continuación:
const string fullPath = "C:\\Projects\\AppDomains\\distrib\\MyLibrary.dll";
// Starting out with a version of MyLibrary.dll which only has 1 method, named Foo()
AssemblyName assemblyName = AssemblyName.GetAssemblyName(fullPath);
AppDomain appDomain = AppDomain.CreateDomain("MyTemp");
appDomain.Load(assemblyName);
AppDomain.Unload(appDomain);
// Breakpoint here; swap out different version of MyLibrary.dll which only has 1 method, named Goo()
AssemblyName assemblyName2 = AssemblyName.GetAssemblyName(fullPath);
AppDomain appDomain2 = AppDomain.CreateDomain("MyTemp2");
Assembly asm2 = appDomain2.Load(assemblyName2);
foreach (Type type in asm2.GetExportedTypes())
{
foreach (MemberInfo memberInfo in type.GetMembers())
{
string name = memberInfo.Name;
// Breakpoint here: Found Foo and but no Goo! I was expecting Goo and no Foo.
}
}
Parece que debe haber ninguna manera el conjunto está siendo tirado en el dominio de aplicación principal entre estas 2 líneas:
appDomain.Load(assemblyName);
AppDomain.Unload(appDomain);
¿podría explicar "se cayó en llamas"? –
Verificaría que realmente está mirando el segundo conjunto. pruébelos como dos nombres de archivo diferentes uno al lado del otro en lugar de hacerlos del mismo nombre. –
¿Los ensambles tienen un nombre fuerte? El GAC tiene un caché de descarga de ensamblaje que se aferra a ensamblajes con nombres fuertes. Puede verificar si está allí ejecutando gacutil/ldl y borrarlo ejecutando gacutil/cdl. –