Compartir a través de


Procedimientos recomendados para la carga de ensamblados

Nota:

Este artículo es específico de .NET Framework. No se aplica a implementaciones más recientes de .NET, incluidas .NET 6 y versiones posteriores.

En este artículo se describen formas de evitar problemas de identidad de tipo que pueden provocar InvalidCastException, MissingMethodExceptiony otros errores. En el artículo se describen las siguientes recomendaciones:

La primera recomendación, comprender las ventajas y desventajas de los contextos de carga, proporciona información en segundo plano para las demás recomendaciones, ya que todas dependen de un conocimiento de contextos de carga.

Comprender las ventajas y desventajas de los contextos de carga

Dentro de un dominio de aplicación, los ensamblados se pueden cargar en uno de los tres contextos o se pueden cargar sin contexto:

  • El contexto de carga predeterminado contiene ensamblados que se encuentran al sondear la caché global de ensamblados, el almacén de ensamblado de host si el runtime está hospedado (por ejemplo, en SQL Server) y el ApplicationBase y PrivateBinPath del dominio de aplicación. La mayoría de las sobrecargas del método Load cargan ensamblajes en este contexto.

  • El contexto de origen de carga contiene ensamblados que se cargan desde ubicaciones en las que no busca el cargador. Por ejemplo, los complementos pueden instalarse en un directorio que no está en la ruta de acceso de la aplicación. Assembly.LoadFrom, AppDomain.CreateInstanceFromy AppDomain.ExecuteAssembly son ejemplos de métodos que se cargan por ruta de acceso.

  • El contexto de solo reflexión contiene ensamblados cargados con los métodos ReflectionOnlyLoad y ReflectionOnlyLoadFrom. No se puede ejecutar código en este contexto, por lo que no se describe aquí. Para obtener más información, vea Cómo: Cargar ensamblados en el contexto de solo reflexión.

  • Si ha generado un ensamblado dinámico transitorio mediante la reflexión de la emisión, el ensamblado no está en ningún contexto. Además, la mayoría de los ensamblados que se cargan mediante el LoadFile método se cargan sin contexto y los ensamblados que se cargan desde matrices de bytes se cargan sin contexto a menos que su identidad (después de aplicar la directiva) establezca que se encuentran en la caché global de ensamblados.

Los contextos de ejecución tienen ventajas y desventajas, como se describe en las secciones siguientes.

Contexto de carga predeterminado

Cuando los ensamblados se cargan en el contexto de carga predeterminado, sus dependencias se cargan automáticamente. Las dependencias que se cargan en el contexto de carga predeterminado se encuentran automáticamente para los ensamblados en el contexto de carga predeterminado o en el contexto de origen de carga. La carga por identidad del ensamblado aumenta la estabilidad de las aplicaciones al garantizar que no se usen versiones desconocidas de ensamblados (vea la sección Evite los enlaces en nombres de ensamblado parciales).

El uso del contexto de carga predeterminado tiene las siguientes desventajas:

  • Las dependencias que se cargan en otros contextos no están disponibles.

  • No se pueden cargar ensamblados desde ubicaciones fuera de la ruta de sondeo en el contexto de carga predeterminado.

Load-From Contexto

El contexto de origen de carga permite cargar un ensamblado desde una ruta de acceso que no está en la ruta de acceso de la aplicación y que, por tanto, no está incluida en el sondeo. Permite encontrar y cargar dependencias desde esa ruta de acceso porque el contexto mantiene la información de la ruta de acceso. Además, los ensamblados de este contexto pueden usar dependencias que se cargan en el contexto de carga predeterminado.

La carga de ensamblados mediante el método Assembly.LoadFrom, o alguno de los demás métodos que cargan por ruta de acceso, tiene las siguientes desventajas:

  • Si hay un ensamblado con la misma identidad ya cargado en el contexto de origen de carga, LoadFrom devuelve el ensamblado cargado aunque se haya especificado otra ruta de acceso.

  • Si un ensamblado se carga con LoadFromy, después, un ensamblado en el contexto de carga predeterminado intenta cargar el mismo ensamblado por nombre para mostrar, se produce un error en el intento de carga. Esto puede ocurrir cuando se deserializa un ensamblado.

  • Si un ensamblado se carga con LoadFrom y la ruta de acceso de sondeo incluye un ensamblado con la misma identidad pero en otra ubicación, puede producirse una InvalidCastException, MissingMethodException u otro comportamiento inesperado.

  • LoadFrom exige FileIOPermissionAccess.Read y FileIOPermissionAccess.PathDiscovery, o WebPermission, en la ruta especificada.

  • Si existe una imagen nativa para el ensamblado, no se usa.

  • El ensamblado no se puede cargar en modo neutro de dominio.

  • En las versiones 1.0 y 1.1 de .NET Framework, no se aplica la directiva.

Sin contexto

La carga sin contexto es la única opción para los ensamblados transitorios generados con la emisión de la reflexión. Cargar sin contexto es la única manera de cargar varios ensamblados que tienen la misma identidad en un dominio de aplicación. Se evita el costo del sondeo.

Los ensamblados que se cargan desde matrices de bytes se cargan sin contexto a menos que la identidad del ensamblado, que se establece cuando se aplica la directiva, coincide con la identidad de un ensamblado en la caché global de ensamblados; en ese caso, el ensamblado se carga desde la caché global de ensamblados.

La carga de ensamblajes sin contexto tiene las siguientes desventajas:

  • Otros ensamblados no se pueden enlazar a ensamblados que se cargan sin contexto, a menos que controle el AppDomain.AssemblyResolve evento.

  • Las dependencias no se cargan automáticamente. Puede precargarlos sin contexto, precargarlos en el contexto de carga predeterminado, o cargarlos controlando el evento AppDomain.AssemblyResolve.

  • Cargar varios ensamblados con la misma identidad sin contexto puede provocar problemas de identidad de tipo similares a los causados por la carga de ensamblados con la misma identidad en varios contextos. Vea Evite la carga de un ensamblado en varios contextos.

  • Si existe una imagen nativa para el ensamblado, no se usa.

  • El ensamblado no se puede cargar en modo neutro de dominio.

  • En las versiones 1.0 y 1.1 de .NET Framework, no se aplica la directiva.

Evite los enlaces en nombres de ensamblado parciales

El enlace de nombres parcial se produce cuando se especifica solo parte del nombre de presentación del ensamblado (FullName) al cargar un ensamblado. Por ejemplo, puede llamar al método Assembly.Load solo con el nombre simple del ensamblado, omitiendo la versión, la cultura y el token de clave pública. O bien, podría llamar al Assembly.LoadWithPartialName método , que primero llama al Assembly.Load método y, si no encuentra el ensamblado, busca en la caché global de ensamblados y carga la versión más reciente disponible del ensamblado.

Los enlaces de nombre parcial pueden producir muchos problemas, incluidos los siguientes:

  • El Assembly.LoadWithPartialName método puede cargar un ensamblado diferente con el mismo nombre simple. Por ejemplo, dos aplicaciones pueden instalar dos ensamblados completamente diferentes que ambos tienen el nombre GraphicsLibrary simple en la caché global de ensamblados.

  • El ensamblado cargado realmente podría no ser compatible con versiones anteriores. Por ejemplo, no especificar la versión podría dar lugar a la carga de una versión mucho posterior a la versión que el programa se escribió originalmente para su uso. Los cambios en la versión posterior podrían provocar errores en la aplicación.

  • El ensamblado cargado realmente podría no ser compatible con versiones posteriores. Por ejemplo, es posible que haya compilado y probado la aplicación con la versión más reciente de un ensamblado, pero el enlace parcial podría cargar una versión mucho anterior que carece de características que usa la aplicación.

  • La instalación de nuevas aplicaciones puede interrumpir las aplicaciones existentes. Una aplicación que usa el LoadWithPartialName método se puede romper instalando una versión más reciente y incompatible de un ensamblado compartido.

  • Se puede producir una carga de dependencias inesperada. Si carga dos ensamblados que comparten una dependencia, cargarlos con enlace parcial podría resultar en que un ensamblado utilice un componente con el que no fue creado ni probado.

Debido a los problemas que puede causar, el LoadWithPartialName método se ha marcado como obsoleto. Se recomienda usar el método Assembly.Load en su lugar y especificar nombres para mostrar de ensamblado completos. Consulte Descripción de las ventajas y desventajas de los contextos de carga y considere la posibilidad de cambiar al contexto de carga predeterminado.

Si desea usar el método porque facilita la LoadWithPartialName carga de ensamblados, tenga en cuenta que si la aplicación produce un error con un mensaje de error que identifica el ensamblado que falta es probable que proporcione una mejor experiencia de usuario que usar automáticamente una versión desconocida del ensamblado, lo que podría provocar un comportamiento impredecible y agujeros de seguridad.

Evite la carga de un ensamblado en varios contextos

La carga de un ensamblado en varios contextos puede ocasionar problemas de identidad de tipos. Si el mismo tipo se carga desde el mismo ensamblado en dos contextos diferentes, es como si se hubieran cargado dos tipos diferentes con el mismo nombre. Si intenta convertir un tipo en el otro, se produce una InvalidCastException con el confuso mensaje de que no se puede convertir el tipo MyType en el tipo MyType.

Por ejemplo, supongamos que la ICommunicate interfaz se declara en un ensamblado denominado Utility, al que hace referencia el programa y también a otros ensamblados que carga el programa. Estos otros ensamblados contienen tipos que implementan la interfaz ICommunicate, lo que permite que el programa los utilice.

Ahora tenga en cuenta lo que sucede cuando se ejecuta el programa. Los ensamblados a los que hace referencia el programa se cargan en el contexto de carga predeterminado. Si carga un ensamblado de destino por su identidad, con el método Load, estará en el contexto de carga predeterminado, igual que sus dependencias. Tanto el programa como el ensamblado de destino usarán el mismo Utility ensamblado.

Sin embargo, supongamos que carga el ensamblado de destino por su ruta de archivo mediante el método LoadFile. El ensamblado se carga sin ningún contexto, por lo que sus dependencias no se cargan automáticamente. Podría tener un controlador para que el evento AppDomain.AssemblyResolve proporcionara la dependencia y este podría cargar el ensamblado Utility sin contexto con el método LoadFile. Ahora, al crear una instancia de un tipo que está contenido en el ensamblado de destino e intentar asignarla a una variable de tipo ICommunicate, se lanza un InvalidCastException porque el tiempo de ejecución considera que las interfaces ICommunicate en las dos copias del ensamblado Utility son de tipos diferentes.

Hay muchos otros escenarios en los que un ensamblado se puede cargar en varios contextos. El mejor enfoque es evitar los conflictos mediante la reubicación del ensamblado de destino en la ruta de acceso de la aplicación y el empleo del método Load con el nombre para mostrar completo. A continuación, el ensamblado se carga en el contexto de carga predeterminado y ambos ensamblados usan el mismo Utility ensamblado.

Si el ensamblado de destino debe permanecer fuera de la ruta de acceso de la aplicación, puede usar el método LoadFrom para cargarlo en el contexto de origen de carga. Si el ensamblado de destino se compiló con una referencia al ensamblado de la Utility aplicación, usará el Utility ensamblado que la aplicación ha cargado en el contexto de carga predeterminado. Tenga en cuenta que pueden producirse problemas si el ensamblado de destino tiene una dependencia de una copia del ensamblado que se encuentra fuera de la ruta de acceso de la aplicación Utility. Si ese ensamblado se carga en el contexto de carga-desde antes de que la aplicación cargue el ensamblado Utility, fallará la carga de la aplicación.

En la sección Consider Switching to the Default Load Context (Considerar cambiar al contexto de carga predeterminado), se describen las alternativas al uso de cargas por ruta de archivo, como LoadFile y LoadFrom.

Evitar cargar varias versiones de un ensamblado en el mismo contexto

Cargar varias versiones de un ensamblado en un contexto de carga puede provocar problemas de identidad de tipo. Si el mismo tipo se carga desde dos versiones del mismo ensamblado, es como si se hubieran cargado dos tipos diferentes con el mismo nombre. Si intenta convertir un tipo en el otro, se produce una InvalidCastException con el confuso mensaje de que no se puede convertir el tipo MyType en el tipo MyType.

Por ejemplo, el programa podría cargar una versión del Utility ensamblado directamente y, posteriormente, podría cargar otro ensamblado que cargue una versión diferente del Utility ensamblado. O un error de codificación podría provocar dos rutas de acceso de código diferentes en la aplicación para cargar versiones diferentes de un ensamblado.

En el contexto de carga predeterminado, este problema puede producirse cuando se usa el método Assembly.Load y se especifican nombres para mostrar de ensamblado completos que incluyen números de versión diferentes. En el caso de los ensamblados que se cargan sin contexto, el problema puede deberse a que el Assembly.LoadFile método cargue el mismo ensamblado desde diferentes rutas de acceso. El entorno de ejecución considera que dos ensamblados cargados desde diferentes rutas son ensamblados distintos, incluso si sus identidades son iguales.

Además de los problemas de identidad de tipo, varias versiones de un ensamblado pueden provocar un MissingMethodException si un tipo que se carga desde una versión del ensamblado se pasa al código que espera ese tipo de una versión diferente. Por ejemplo, el código podría esperar un método que se agregó a la versión posterior.

Se pueden producir errores más sutiles si el comportamiento del tipo ha cambiado entre versiones. Por ejemplo, un método podría producir una excepción inesperada o devolver un valor inesperado.

Revise cuidadosamente el código para asegurarse de que solo se carga una versión de un ensamblado. Puede usar el AppDomain.GetAssemblies método para determinar qué ensamblados se cargan en cualquier momento dado.

Considere la posibilidad de cambiar al contexto de carga predeterminado

Examine los patrones de carga e implementación de ensamblados de la aplicación. ¿Puede eliminar ensamblados cargados desde matrices de bytes? ¿Puede mover ensamblados a la ruta de acceso de sondeo? Si los ensamblados se encuentran en la caché global de ensamblados o en la ruta de acceso de sondeo del dominio de la aplicación (es decir, su ApplicationBase y PrivateBinPath), puede cargar el ensamblado por su identidad.

Si no es posible colocar todos los ensamblados en la ruta de acceso de sondeo, considere alternativas como usar el modelo de complementos de .NET Framework, colocar los ensamblados en la caché global de ensamblados o crear dominios de aplicación.

Considere la posibilidad de usar el modelo de Add-In de .NET Framework

Si estás utilizando el contexto de carga para implementar complementos, que generalmente no están instalados en la base de la aplicación, utiliza el modelo de complementos de .NET Framework. Este modelo proporciona aislamiento en el nivel de proceso o dominio de aplicación, sin necesidad de administrar los dominios de aplicación usted mismo. Para obtener información sobre el modelo de complemento, vea Complementos y extensibilidad.

Considere la posibilidad de usar la caché global de ensamblados

Coloque ensamblados en la caché global de ensamblados para aprovechar una ruta de ensamblado compartida que esté fuera de la base de la aplicación, sin perder las ventajas del contexto de carga predeterminado ni asumir las desventajas de otros contextos.

Considere la posibilidad de usar dominios de aplicación

Si determina que algunos de los ensamblados no se pueden implementar en la ruta de acceso de sondeo de la aplicación, considere la posibilidad de crear un nuevo dominio de aplicación para esos ensamblados. Utilice un AppDomainSetup para crear el nuevo dominio de aplicación y utilice la AppDomainSetup.ApplicationBase propiedad para especificar la ruta de acceso que contiene los ensamblados que desea cargar. Si tiene varios directorios para sondear, puede establecer en ApplicationBase un directorio raíz y usar la AppDomainSetup.PrivateBinPath propiedad para identificar los subdirectorios que se van a sondear. Como alternativa, puede crear varios dominios de aplicación y establecer la ApplicationBase de cada dominio de aplicación en la ruta de acceso adecuada para sus ensamblados.

Tenga en cuenta que puede usar el Assembly.LoadFrom método para cargar estos ensamblados. Dado que ahora se encuentran en la ruta de acceso de sondeo, se cargarán en el contexto de carga predeterminado en lugar de hacerlo en el contexto de origen de carga. Sin embargo, recomendamos que cambie al método Assembly.Load y proporcione nombres de visualización completos del ensamblado para garantizar que siempre se usen las versiones correctas.

Consulte también