Introducción

Una de las preguntas que más frecuentemente me preguntan en proyectos o entrenamientos de Silverlight es la posibilidad de descargar ensamblados (assemblies) .DLL bajo demanda, es decir que no sean parte del archivo .XAP sin perder la experiencia en el uso de tipos en Visual Studio .NET (Intellisense principalmente).  Menciono esto porque Silverlight soporta Reflection: la serie de clases que nos permiten inspeccionar e invocar tipos de manera dinámica.  El problema con esta opción es que perdemos precisamente la capacidad de early-binding, Intellisense, etc.  En este artículo veremos cómo podemos desacoplar ensamblados externos y descargarlos/cargarlos bajo demanda (sin usar Reflection).

La Solución

Crearemos una nueva solución de Silverlight utilizando la plantilla para Visual Studio .NET 2008.  A la solución le llamaremos DemoDescargaAssembly.  Además agregaremos un nuevo proyecto llamado DemoAssembly de tipo Silverlight Class Library (Biblioteca de Clases de Silverlight) en esta solución.  Al agregar una referencia a DemoAssembly en la aplicación de Silverlight y después compilar tendremos como resultado un archivo llamado DemoDescargaAssembly.xap.  El comportamiento por default de Visual Studio .NET es que el .dll lo agregará dentro del archivo .xap tal y como lo muestra la siguiente figura:

Archivo XAP con DLL incluido

Esto se debe a que automáticamente al agregar la referencia a un .dll en nuestro proyecto de Silverlight, la propiedad Copy Local estará en true.  No obstante si cambiamos el valor a false el comportamiento cambiará ya que el ensamblado no será empaquetado en el archivo .xap, pero nuestra experiencia en el desarrollo sigue sin modificaciones ya que podemos seguir haciendo uso de los miembros del ensamblado gracias a la referencia existente.

Explorador de Soluciones

Al establecer Copy Local a false y recompilar la solución, nuestro archivo .xap no tendrá el ensamblado referenciado.

Archivo XAP sin el DLL

Ahora bien, si implementamos un método público en DemoAssembly que sea invocado desde la aplicación de Silverlight, la aplicación mandará una excepción al no poder localizar el ensamblado necesario para ejecutar esa línea.  A continuación se muestran los fragmentos de código de DemoAssembly.Demo y la Aplicación Silverlight.

DemoAssembly.Demo
public string Saluda(string nombre)
{
    return "Hola " + nombre;
}
Aplicación Silverlight
public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
 
            btn.Click += (s, a) =>
            {
                DemoAssembly.Demo demo = new DemoAssembly.Demo();
                txt.Text = demo.Saluda("Rodrigo");
            };
 
        }
    }

FileNotFoundException

Descarga bajo demanda y clase AssemblyPart

Ya que el objetivo de este artículo es mostrar cómo podemos descargar y cargar ensamblados bajo demanda lo que haremos será copiar el .dll al sitio de origen (fólder ClientBin en la aplicación Web muy probablemente).  Usaremos un objeto de tipo WebClient para poder descargar el archivo DemoAssembly.dll de manera asíncrona por medio de su método OpenReadyAsync() y posteriormente crearemos un ensamblado de manera dinámica para nuestra aplicación Silverlight.  De esta manera nos aseguramos que la anterior línea de código presentada funcione y no mande una excepción.

La clase AssemblyPart representa un ensamblado dentro de la aplicación Silverlight, y su método Load() nos permite cargar un ensamblado a través de un Stream en el mismo Application Domain.  El siguiente fragmento de código muestra la aplicación finalizada en donde podemos apreciar cómo primero se descarga el .dll del servidor y se crea el AssemblyPart para tenerlo disponible cuando se haga clic en el botón.

public partial class Page : UserControl
   {
       public Page()
       {
           InitializeComponent();
 
           btn.Click += (s, a) =>
           {
               DemoAssembly.Demo demo = new DemoAssembly.Demo();
               txt.Text = demo.Saluda("Rodrigo");
           };
 
           WebClient client = new WebClient();
           client.OpenReadCompleted += (s, a) =>
           {
               if (a.Error == null)
               {
                   AssemblyPart assembly = new AssemblyPart();
                   assembly.Load(a.Result);
               }
           };
           client.OpenReadAsync(new Uri("DemoAssembly.dll", UriKind.RelativeOrAbsolute));
 
           
       }
   }

Resumen

La clase AssemblyPart nos permite cargar de manera dinámica un ensamblado y colocarlo en el Application Domain de nuestra aplicación Silverlight para su correcto uso.  Asimismo es necesario establecer la propiedad Copy Local a false para impedir que un ensamblado referenciado se empaquete dentro del archivo .xap.  Esta técnica nos permite crear aplicaciones de Silverlight más livianas y que hagan uso de ensamblados y recursos de manera dinámica.

Demo (Requiere el plugin de Silverlight 2)